summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2022-02-12 05:15:38 -0500
committerJason R. Coombs <jaraco@jaraco.com>2022-02-12 05:15:38 -0500
commitd7b783a4b8b01e58135e40bd9a1db8a82c090982 (patch)
tree19f75949414a1f6942e59fd69e9a1e86f469ea5a
parent5b75de07169ef13952a5ab48e0b8bc15f31d0c37 (diff)
parentce9ddd528a9e2507d3ae794f4d26f6a46d06d025 (diff)
downloadpython-setuptools-git-d7b783a4b8b01e58135e40bd9a1db8a82c090982.tar.gz
Merge branch 'main' into debt/remove-legacy-version
-rw-r--r--.bumpversion.cfg2
-rw-r--r--.codecov.yml3
-rw-r--r--.github/ISSUE_TEMPLATE/bug-report.yml11
-rw-r--r--.github/ISSUE_TEMPLATE/config.yml7
-rw-r--r--.github/ISSUE_TEMPLATE/feature-request.yml2
-rw-r--r--.github/pull_request_template.md4
-rw-r--r--.github/workflows/ci-sage.yml152
-rw-r--r--.github/workflows/main.yml74
-rw-r--r--.gitignore1
-rw-r--r--CHANGES.rst381
-rw-r--r--README.rst14
-rw-r--r--_distutils_hack/__init__.py130
-rw-r--r--bootstrap.py7
-rw-r--r--conftest.py20
-rw-r--r--docs/artwork.rst (renamed from docs/images/README.rst)6
-rw-r--r--docs/build_meta.rst115
-rw-r--r--docs/conf.py30
-rw-r--r--docs/deprecated/distutils-legacy.rst2
-rw-r--r--docs/deprecated/distutils/_setuptools_disclaimer.rst2
-rw-r--r--docs/deprecated/distutils/apiref.rst2
-rw-r--r--docs/deprecated/functionalities.rst2
-rw-r--r--docs/development/developer-guide.rst23
-rw-r--r--docs/images/banner-640x320.pngbin15763 -> 0 bytes
-rw-r--r--docs/images/banner-640x320.svg131
-rw-r--r--docs/images/banner-negative-640x320.pngbin18004 -> 0 bytes
-rw-r--r--docs/images/banner-negative-640x320.svg140
-rw-r--r--docs/images/favicon.icobin35622 -> 0 bytes
-rw-r--r--docs/images/favicon.svg64
-rw-r--r--docs/images/favicon.xcfbin18175 -> 0 bytes
-rw-r--r--docs/images/logo-demo-editable-inkscape.pngbin90174 -> 0 bytes
-rw-r--r--docs/images/logo-demo.svg150
-rw-r--r--docs/images/logo-editable-inkscape.pngbin13249 -> 0 bytes
-rw-r--r--docs/images/logo-inline-negative.pngbin8985 -> 0 bytes
-rw-r--r--docs/images/logo-inline-negative.svg136
-rw-r--r--docs/images/logo-inline.pngbin8216 -> 0 bytes
-rw-r--r--docs/images/logo-inline.svg127
-rw-r--r--docs/images/logo-negative.pngbin14594 -> 0 bytes
-rw-r--r--docs/images/logo-negative.svg137
-rw-r--r--docs/images/logo-over-white.pngbin13915 -> 0 bytes
-rw-r--r--docs/images/logo-over-white.svg137
-rw-r--r--docs/images/logo-symbol-only.pngbin8506 -> 0 bytes
-rw-r--r--docs/images/logo-symbol-only.svg62
-rw-r--r--docs/images/logo-text-only.pngbin4705 -> 0 bytes
-rw-r--r--docs/images/logo-text-only.svg111
-rw-r--r--docs/images/logo.pngbin13208 -> 0 bytes
-rw-r--r--docs/images/logo.svg128
-rw-r--r--docs/index.rst2
-rw-r--r--docs/pkg_resources.rst2
-rw-r--r--docs/setuptools.rst10
-rw-r--r--docs/userguide/declarative_config.rst4
-rw-r--r--docs/userguide/dependency_management.rst4
-rw-r--r--docs/userguide/distribution.rst2
-rw-r--r--docs/userguide/quickstart.rst41
-rw-r--r--exercises.py6
-rw-r--r--pavement.py70
-rw-r--r--pkg_resources/__init__.py53
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/DESCRIPTION.rst227
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/METADATA254
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/RECORD10
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/REQUESTED0
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/WHEEL6
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/metadata.json1
-rw-r--r--pkg_resources/_vendor/appdirs-1.4.3.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/LICENSE13
-rw-r--r--pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/METADATA86
-rw-r--r--pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/RECORD75
-rw-r--r--pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED0
-rw-r--r--pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/WHEEL5
-rw-r--r--pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/importlib_resources/__init__.py36
-rw-r--r--pkg_resources/_vendor/importlib_resources/_adapters.py170
-rw-r--r--pkg_resources/_vendor/importlib_resources/_common.py104
-rw-r--r--pkg_resources/_vendor/importlib_resources/_compat.py98
-rw-r--r--pkg_resources/_vendor/importlib_resources/_itertools.py35
-rw-r--r--pkg_resources/_vendor/importlib_resources/_legacy.py121
-rw-r--r--pkg_resources/_vendor/importlib_resources/abc.py137
-rw-r--r--pkg_resources/_vendor/importlib_resources/py.typed0
-rw-r--r--pkg_resources/_vendor/importlib_resources/readers.py122
-rw-r--r--pkg_resources/_vendor/importlib_resources/simple.py116
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/_compat.py19
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data01/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data01/binary.filebin0 -> 4 bytes
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/binary.filebin0 -> 4 bytes
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data01/utf-16.filebin0 -> 44 bytes
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data01/utf-8.file1
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data02/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data02/one/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data02/one/resource1.txt1
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data02/two/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/data02/two/resource2.txt1
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/namespacedata01/binary.filebin0 -> 4 bytes
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-16.filebin0 -> 44 bytes
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-8.file1
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_compatibilty_files.py102
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_contents.py43
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_files.py46
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_open.py81
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_path.py64
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_read.py76
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_reader.py128
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/test_resource.py252
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/update-zips.py53
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/util.py178
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/zipdata01/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zipbin0 -> 876 bytes
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/zipdata02/__init__.py0
-rw-r--r--pkg_resources/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zipbin0 -> 698 bytes
-rw-r--r--pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/LICENSE19
-rw-r--r--pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/METADATA52
-rw-r--r--pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/RECORD8
-rw-r--r--pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/WHEEL5
-rw-r--r--pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE19
-rw-r--r--pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/METADATA58
-rw-r--r--pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/RECORD8
-rw-r--r--pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL5
-rw-r--r--pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/LICENSE19
-rw-r--r--pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/METADATA55
-rw-r--r--pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/RECORD10
-rw-r--r--pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED0
-rw-r--r--pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/WHEEL5
-rw-r--r--pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/jaraco/__init__.py0
-rw-r--r--pkg_resources/_vendor/jaraco/context.py213
-rw-r--r--pkg_resources/_vendor/jaraco/functools.py525
-rw-r--r--pkg_resources/_vendor/jaraco/text/Lorem ipsum.txt2
-rw-r--r--pkg_resources/_vendor/jaraco/text/__init__.py599
-rw-r--r--pkg_resources/_vendor/more_itertools-8.12.0.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/more_itertools-8.12.0.dist-info/LICENSE19
-rw-r--r--pkg_resources/_vendor/more_itertools-8.12.0.dist-info/METADATA521
-rw-r--r--pkg_resources/_vendor/more_itertools-8.12.0.dist-info/RECORD16
-rw-r--r--pkg_resources/_vendor/more_itertools-8.12.0.dist-info/WHEEL5
-rw-r--r--pkg_resources/_vendor/more_itertools-8.12.0.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/more_itertools/__init__.py4
-rw-r--r--pkg_resources/_vendor/more_itertools/__init__.pyi2
-rw-r--r--pkg_resources/_vendor/more_itertools/more.py4316
-rw-r--r--pkg_resources/_vendor/more_itertools/more.pyi664
-rw-r--r--pkg_resources/_vendor/more_itertools/py.typed0
-rw-r--r--pkg_resources/_vendor/more_itertools/recipes.py698
-rw-r--r--pkg_resources/_vendor/more_itertools/recipes.pyi112
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE3
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.APACHE177
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.BSD23
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/METADATA446
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/RECORD32
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/REQUESTED0
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/WHEEL5
-rw-r--r--pkg_resources/_vendor/packaging-21.2.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/packaging/__about__.py5
-rw-r--r--pkg_resources/_vendor/packaging/__init__.py1
-rw-r--r--pkg_resources/_vendor/packaging/_compat.py38
-rw-r--r--pkg_resources/_vendor/packaging/_manylinux.py301
-rw-r--r--pkg_resources/_vendor/packaging/_musllinux.py136
-rw-r--r--pkg_resources/_vendor/packaging/_structures.py59
-rw-r--r--pkg_resources/_vendor/packaging/_typing.py48
-rw-r--r--pkg_resources/_vendor/packaging/markers.py136
-rw-r--r--pkg_resources/_vendor/packaging/requirements.py71
-rw-r--r--pkg_resources/_vendor/packaging/specifiers.py313
-rw-r--r--pkg_resources/_vendor/packaging/tags.py559
-rw-r--r--pkg_resources/_vendor/packaging/utils.py125
-rw-r--r--pkg_resources/_vendor/packaging/version.py275
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst3
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt18
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/METADATA30
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/RECORD11
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/REQUESTED0
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/WHEEL6
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/metadata.json1
-rw-r--r--pkg_resources/_vendor/pyparsing-2.2.1.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/pyparsing.py2
-rw-r--r--pkg_resources/_vendor/vendored.txt7
-rw-r--r--pkg_resources/_vendor/zipp-3.7.0.dist-info/INSTALLER1
-rw-r--r--pkg_resources/_vendor/zipp-3.7.0.dist-info/LICENSE19
-rw-r--r--pkg_resources/_vendor/zipp-3.7.0.dist-info/METADATA58
-rw-r--r--pkg_resources/_vendor/zipp-3.7.0.dist-info/RECORD9
-rw-r--r--pkg_resources/_vendor/zipp-3.7.0.dist-info/REQUESTED0
-rw-r--r--pkg_resources/_vendor/zipp-3.7.0.dist-info/WHEEL5
-rw-r--r--pkg_resources/_vendor/zipp-3.7.0.dist-info/top_level.txt1
-rw-r--r--pkg_resources/_vendor/zipp.py329
-rw-r--r--pkg_resources/extern/__init__.py5
-rw-r--r--pkg_resources/tests/test_resources.py2
-rw-r--r--pyproject.toml2
-rw-r--r--pytest.ini18
-rw-r--r--setup.cfg33
-rwxr-xr-xsetup.py2
-rw-r--r--setuptools/__init__.py12
-rw-r--r--setuptools/_distutils/__init__.py11
-rw-r--r--setuptools/_distutils/_collections.py56
-rw-r--r--setuptools/_distutils/_msvccompiler.py4
-rw-r--r--setuptools/_distutils/bcppcompiler.py2
-rw-r--r--setuptools/_distutils/command/bdist_msi.py2
-rw-r--r--setuptools/_distutils/command/build_ext.py4
-rw-r--r--setuptools/_distutils/command/check.py2
-rw-r--r--setuptools/_distutils/command/install.py263
-rw-r--r--setuptools/_distutils/command/install_egg_info.py15
-rw-r--r--setuptools/_distutils/core.py55
-rw-r--r--setuptools/_distutils/cygwinccompiler.py156
-rw-r--r--setuptools/_distutils/log.py6
-rw-r--r--setuptools/_distutils/msvc9compiler.py6
-rw-r--r--setuptools/_distutils/msvccompiler.py2
-rw-r--r--setuptools/_distutils/spawn.py2
-rw-r--r--setuptools/_distutils/sysconfig.py67
-rw-r--r--setuptools/_distutils/tests/py38compat.py9
-rw-r--r--setuptools/_distutils/tests/test_archive_util.py14
-rw-r--r--setuptools/_distutils/tests/test_bdist.py2
-rw-r--r--setuptools/_distutils/tests/test_bdist_dumb.py2
-rw-r--r--setuptools/_distutils/tests/test_bdist_msi.py2
-rw-r--r--setuptools/_distutils/tests/test_bdist_rpm.py11
-rw-r--r--setuptools/_distutils/tests/test_bdist_wininst.py2
-rw-r--r--setuptools/_distutils/tests/test_build.py2
-rw-r--r--setuptools/_distutils/tests/test_build_clib.py2
-rw-r--r--setuptools/_distutils/tests/test_build_ext.py12
-rw-r--r--setuptools/_distutils/tests/test_build_py.py2
-rw-r--r--setuptools/_distutils/tests/test_build_scripts.py2
-rw-r--r--setuptools/_distutils/tests/test_check.py2
-rw-r--r--setuptools/_distutils/tests/test_clean.py2
-rw-r--r--setuptools/_distutils/tests/test_cmd.py2
-rw-r--r--setuptools/_distutils/tests/test_config.py4
-rw-r--r--setuptools/_distutils/tests/test_config_cmd.py2
-rw-r--r--setuptools/_distutils/tests/test_core.py27
-rw-r--r--setuptools/_distutils/tests/test_cygwinccompiler.py70
-rw-r--r--setuptools/_distutils/tests/test_dep_util.py2
-rw-r--r--setuptools/_distutils/tests/test_dir_util.py2
-rw-r--r--setuptools/_distutils/tests/test_dist.py4
-rw-r--r--setuptools/_distutils/tests/test_extension.py2
-rw-r--r--setuptools/_distutils/tests/test_file_util.py2
-rw-r--r--setuptools/_distutils/tests/test_filelist.py4
-rw-r--r--setuptools/_distutils/tests/test_install.py19
-rw-r--r--setuptools/_distutils/tests/test_install_data.py2
-rw-r--r--setuptools/_distutils/tests/test_install_headers.py2
-rw-r--r--setuptools/_distutils/tests/test_install_lib.py2
-rw-r--r--setuptools/_distutils/tests/test_install_scripts.py2
-rw-r--r--setuptools/_distutils/tests/test_log.py2
-rw-r--r--setuptools/_distutils/tests/test_msvc9compiler.py2
-rw-r--r--setuptools/_distutils/tests/test_msvccompiler.py4
-rw-r--r--setuptools/_distutils/tests/test_register.py2
-rw-r--r--setuptools/_distutils/tests/test_sdist.py13
-rw-r--r--setuptools/_distutils/tests/test_spawn.py2
-rw-r--r--setuptools/_distutils/tests/test_sysconfig.py17
-rw-r--r--setuptools/_distutils/tests/test_text_file.py2
-rw-r--r--setuptools/_distutils/tests/test_unixccompiler.py9
-rw-r--r--setuptools/_distutils/tests/test_upload.py2
-rw-r--r--setuptools/_distutils/tests/test_util.py127
-rw-r--r--setuptools/_distutils/tests/test_version.py10
-rw-r--r--setuptools/_distutils/tests/unix_compat.py16
-rw-r--r--setuptools/_distutils/util.py131
-rw-r--r--setuptools/_distutils/version.py28
-rw-r--r--setuptools/_distutils/versionpredicate.py7
-rw-r--r--setuptools/_importlib.py13
-rw-r--r--setuptools/_path.py7
-rw-r--r--setuptools/_reqs.py19
-rw-r--r--setuptools/_vendor/importlib_metadata-4.10.1.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/importlib_metadata-4.10.1.dist-info/LICENSE13
-rw-r--r--setuptools/_vendor/importlib_metadata-4.10.1.dist-info/METADATA118
-rw-r--r--setuptools/_vendor/importlib_metadata-4.10.1.dist-info/RECORD24
-rw-r--r--setuptools/_vendor/importlib_metadata-4.10.1.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/importlib_metadata-4.10.1.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/importlib_metadata-4.10.1.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/importlib_metadata/__init__.py1051
-rw-r--r--setuptools/_vendor/importlib_metadata/_adapters.py68
-rw-r--r--setuptools/_vendor/importlib_metadata/_collections.py30
-rw-r--r--setuptools/_vendor/importlib_metadata/_compat.py71
-rw-r--r--setuptools/_vendor/importlib_metadata/_functools.py104
-rw-r--r--setuptools/_vendor/importlib_metadata/_itertools.py73
-rw-r--r--setuptools/_vendor/importlib_metadata/_meta.py48
-rw-r--r--setuptools/_vendor/importlib_metadata/_text.py99
-rw-r--r--setuptools/_vendor/importlib_metadata/py.typed0
-rw-r--r--setuptools/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/importlib_resources-5.4.0.dist-info/LICENSE13
-rw-r--r--setuptools/_vendor/importlib_resources-5.4.0.dist-info/METADATA86
-rw-r--r--setuptools/_vendor/importlib_resources-5.4.0.dist-info/RECORD75
-rw-r--r--setuptools/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/importlib_resources-5.4.0.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/importlib_resources/__init__.py36
-rw-r--r--setuptools/_vendor/importlib_resources/_adapters.py170
-rw-r--r--setuptools/_vendor/importlib_resources/_common.py104
-rw-r--r--setuptools/_vendor/importlib_resources/_compat.py98
-rw-r--r--setuptools/_vendor/importlib_resources/_itertools.py35
-rw-r--r--setuptools/_vendor/importlib_resources/_legacy.py121
-rw-r--r--setuptools/_vendor/importlib_resources/abc.py137
-rw-r--r--setuptools/_vendor/importlib_resources/py.typed0
-rw-r--r--setuptools/_vendor/importlib_resources/readers.py122
-rw-r--r--setuptools/_vendor/importlib_resources/simple.py116
-rw-r--r--setuptools/_vendor/importlib_resources/tests/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/_compat.py19
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data01/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data01/binary.filebin0 -> 4 bytes
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data01/subdirectory/binary.filebin0 -> 4 bytes
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data01/utf-16.filebin0 -> 44 bytes
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data01/utf-8.file1
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data02/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data02/one/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data02/one/resource1.txt1
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data02/two/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/data02/two/resource2.txt1
-rw-r--r--setuptools/_vendor/importlib_resources/tests/namespacedata01/binary.filebin0 -> 4 bytes
-rw-r--r--setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-16.filebin0 -> 44 bytes
-rw-r--r--setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-8.file1
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_compatibilty_files.py102
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_contents.py43
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_files.py46
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_open.py81
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_path.py64
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_read.py76
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_reader.py128
-rw-r--r--setuptools/_vendor/importlib_resources/tests/test_resource.py252
-rw-r--r--setuptools/_vendor/importlib_resources/tests/update-zips.py53
-rw-r--r--setuptools/_vendor/importlib_resources/tests/util.py178
-rw-r--r--setuptools/_vendor/importlib_resources/tests/zipdata01/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zipbin0 -> 876 bytes
-rw-r--r--setuptools/_vendor/importlib_resources/tests/zipdata02/__init__.py0
-rw-r--r--setuptools/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zipbin0 -> 698 bytes
-rw-r--r--setuptools/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/jaraco.context-4.1.1.dist-info/LICENSE19
-rw-r--r--setuptools/_vendor/jaraco.context-4.1.1.dist-info/METADATA52
-rw-r--r--setuptools/_vendor/jaraco.context-4.1.1.dist-info/RECORD8
-rw-r--r--setuptools/_vendor/jaraco.context-4.1.1.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE19
-rw-r--r--setuptools/_vendor/jaraco.functools-3.5.0.dist-info/METADATA58
-rw-r--r--setuptools/_vendor/jaraco.functools-3.5.0.dist-info/RECORD8
-rw-r--r--setuptools/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/jaraco.text-3.7.0.dist-info/LICENSE19
-rw-r--r--setuptools/_vendor/jaraco.text-3.7.0.dist-info/METADATA55
-rw-r--r--setuptools/_vendor/jaraco.text-3.7.0.dist-info/RECORD10
-rw-r--r--setuptools/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/jaraco.text-3.7.0.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/jaraco/__init__.py0
-rw-r--r--setuptools/_vendor/jaraco/context.py213
-rw-r--r--setuptools/_vendor/jaraco/functools.py525
-rw-r--r--setuptools/_vendor/jaraco/text/Lorem ipsum.txt2
-rw-r--r--setuptools/_vendor/jaraco/text/__init__.py599
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE19
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA462
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD17
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/more_itertools/more.py3
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA157
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE19
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD9
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL6
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/LICENSE3
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE177
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD23
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/METADATA446
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/RECORD32
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/packaging/__about__.py5
-rw-r--r--setuptools/_vendor/packaging/__init__.py1
-rw-r--r--setuptools/_vendor/packaging/_compat.py38
-rw-r--r--setuptools/_vendor/packaging/_manylinux.py301
-rw-r--r--setuptools/_vendor/packaging/_musllinux.py136
-rw-r--r--setuptools/_vendor/packaging/_structures.py59
-rw-r--r--setuptools/_vendor/packaging/_typing.py48
-rw-r--r--setuptools/_vendor/packaging/markers.py136
-rw-r--r--setuptools/_vendor/packaging/requirements.py71
-rw-r--r--setuptools/_vendor/packaging/specifiers.py313
-rw-r--r--setuptools/_vendor/packaging/tags.py559
-rw-r--r--setuptools/_vendor/packaging/utils.py125
-rw-r--r--setuptools/_vendor/packaging/version.py275
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst3
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt18
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA30
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD11
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL6
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json1
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/pyparsing.py2
-rw-r--r--setuptools/_vendor/vendored.txt7
-rw-r--r--setuptools/_vendor/zipp-3.7.0.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/zipp-3.7.0.dist-info/LICENSE19
-rw-r--r--setuptools/_vendor/zipp-3.7.0.dist-info/METADATA58
-rw-r--r--setuptools/_vendor/zipp-3.7.0.dist-info/RECORD9
-rw-r--r--setuptools/_vendor/zipp-3.7.0.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/zipp-3.7.0.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/zipp-3.7.0.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/zipp.py329
-rw-r--r--setuptools/archive_util.py2
-rw-r--r--setuptools/build_meta.py5
-rw-r--r--setuptools/command/bdist_egg.py3
-rw-r--r--setuptools/command/easy_install.py71
-rw-r--r--setuptools/command/egg_info.py10
-rw-r--r--setuptools/command/install_egg_info.py3
-rw-r--r--setuptools/command/install_scripts.py3
-rw-r--r--setuptools/config.py6
-rw-r--r--setuptools/depends.py9
-rw-r--r--setuptools/dist.py59
-rw-r--r--setuptools/extension.py2
-rw-r--r--setuptools/extern/__init__.py5
-rw-r--r--setuptools/installer.py7
-rw-r--r--setuptools/logging.py36
-rw-r--r--setuptools/package_index.py12
-rw-r--r--setuptools/tests/contexts.py27
-rw-r--r--setuptools/tests/environment.py18
-rw-r--r--setuptools/tests/fixtures.py83
-rw-r--r--setuptools/tests/integration/__init__.py0
-rw-r--r--setuptools/tests/integration/helpers.py61
-rw-r--r--setuptools/tests/integration/test_pip_install_sdist.py218
-rw-r--r--setuptools/tests/requirements.txt14
-rw-r--r--setuptools/tests/test_bdist_deprecations.py2
-rw-r--r--setuptools/tests/test_bdist_egg.py3
-rw-r--r--setuptools/tests/test_build_py.py3
-rw-r--r--setuptools/tests/test_develop.py25
-rw-r--r--setuptools/tests/test_dist.py5
-rw-r--r--setuptools/tests/test_distutils_adoption.py73
-rw-r--r--setuptools/tests/test_easy_install.py53
-rw-r--r--setuptools/tests/test_logging.py36
-rw-r--r--setuptools/tests/test_setuptools.py26
-rw-r--r--setuptools/tests/test_sphinx_upload_docs.py1
-rw-r--r--setuptools/tests/test_test.py110
-rw-r--r--setuptools/tests/test_upload_docs.py2
-rw-r--r--setuptools/tests/test_virtualenv.py98
-rw-r--r--setuptools/tests/test_wheel.py33
-rw-r--r--setuptools/wheel.py6
-rw-r--r--tools/finalize.py11
-rw-r--r--tools/msvc-build-launcher-arm64.cmd (renamed from msvc-build-launcher-arm64.cmd)0
-rw-r--r--tools/msvc-build-launcher.cmd (renamed from msvc-build-launcher.cmd)0
-rw-r--r--tools/towncrier_template.rst (renamed from towncrier_template.rst)0
-rw-r--r--tools/vendored.py127
-rw-r--r--tox.ini27
447 files changed, 24389 insertions, 4095 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 3be75098..7f466d4f 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
[bumpversion]
-current_version = 59.0.1
+current_version = 60.8.2
commit = True
tag = True
diff --git a/.codecov.yml b/.codecov.yml
index 7510dfc6..51b248ba 100644
--- a/.codecov.yml
+++ b/.codecov.yml
@@ -2,4 +2,5 @@ comment: false
coverage:
status:
project:
- threshold: 0.5%
+ default:
+ threshold: 0.5%
diff --git a/.github/ISSUE_TEMPLATE/bug-report.yml b/.github/ISSUE_TEMPLATE/bug-report.yml
index 73911ec8..672acd18 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.yml
+++ b/.github/ISSUE_TEMPLATE/bug-report.yml
@@ -115,15 +115,4 @@ body:
validations:
required: true
-
-- type: checkboxes
- attributes:
- label: Code of Conduct
- description: |
- Read the [PSF Code of Conduct][CoC] first.
-
- [CoC]: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
- options:
- - label: I agree to follow the PSF Code of Conduct
- required: true
...
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index dde102ca..ebc2d339 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,3 @@
-# Ref: https://help.github.com/en/github/building-a-strong-community/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
-blank_issues_enabled: false # default: true
contact_links:
- name: 🤔 Have questions or need support?
url: https://github.com/pypa/setuptools/discussions
@@ -9,7 +7,6 @@ contact_links:
about: |
Please ask typical Q&A here: general ideas for Python packaging,
questions about structuring projects and so on
-- name: >-
- 💬 IRC: #pypa @ Freenode
- url: https://webchat.freenode.net/#pypa
+- name: 💬 Discord (chat)
+ url: https://discord.com/invite/pypa
about: Chat with devs
diff --git a/.github/ISSUE_TEMPLATE/feature-request.yml b/.github/ISSUE_TEMPLATE/feature-request.yml
index 88ae6741..7bd47681 100644
--- a/.github/ISSUE_TEMPLATE/feature-request.yml
+++ b/.github/ISSUE_TEMPLATE/feature-request.yml
@@ -73,7 +73,7 @@ body:
Have you tried to workaround the problem using other tools? Or a
different approach to solving this issue? Please elaborate here.
placeholder: >-
- I tried doing X, Y and Z. But they are subobpimal because of P.
+ I tried doing X, Y and Z. But they are suboptimal because of P.
- type: textarea
attributes:
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index ef5a98e7..41ecde17 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -1,4 +1,4 @@
-<!-- First time contributors: Take a moment to review https://setuptools.readthedocs.io/en/latest/development/developer-guide.html! -->
+<!-- First time contributors: Take a moment to review https://setuptools.pypa.io/en/latest/development/developer-guide.html! -->
<!-- Remove sections if not applicable -->
## Summary of changes
@@ -15,4 +15,4 @@ Closes <!-- issue number here -->
[`changelog.d/`]: https://github.com/pypa/setuptools/tree/master/changelog.d
[PR docs]:
-https://setuptools.readthedocs.io/en/latest/development/developer-guide.html#making-a-pull-request
+https://setuptools.pypa.io/en/latest/development/developer-guide.html#making-a-pull-request
diff --git a/.github/workflows/ci-sage.yml b/.github/workflows/ci-sage.yml
new file mode 100644
index 00000000..b802a58c
--- /dev/null
+++ b/.github/workflows/ci-sage.yml
@@ -0,0 +1,152 @@
+name: Run Sage CI for Linux
+
+## This GitHub Actions workflow provides:
+##
+## - portability testing, by building and testing this project on many platforms
+##
+## - continuous integration, by building and testing other software
+## that depends on this project.
+##
+## It runs on every push of a tag to the GitHub repository.
+##
+## The testing can be monitored in the "Actions" tab of the GitHub repository.
+##
+## After all jobs have finished (or are canceled) and a short delay,
+## tar files of all logs are made available as "build artifacts".
+##
+## This GitHub Actions workflow uses the portability testing framework
+## of SageMath (https://www.sagemath.org/). For more information, see
+## https://doc.sagemath.org/html/en/developer/portability_testing.html
+
+## The workflow consists of two jobs:
+##
+## - First, it builds a source distribution of the project
+## and generates a script "update-pkgs.sh". It uploads them
+## as a build artifact named upstream.
+##
+## - Second, it checks out a copy of the SageMath source tree.
+## It downloads the upstream artifact and replaces the project's
+## package in the SageMath distribution by the newly packaged one
+## from the upstream artifact, by running the script "update-pkgs.sh".
+## Then it builds a small portion of the Sage distribution.
+##
+## Many copies of the second step are run in parallel for each of the tested
+## systems/configurations.
+
+on:
+ push:
+ tags:
+ - '*'
+ workflow_dispatch:
+ # Allow to run manually
+
+env:
+ # Ubuntu packages to install so that the project's "setup.py sdist" can succeed
+ DIST_PREREQ: python3
+ # Name of this project in the Sage distribution
+ SPKG: setuptools
+ # Sage distribution packages to build
+ TARGETS_PRE: build/make/Makefile
+ TARGETS: setuptools pyzmq
+ TARGETS_OPTIONAL: build/make/Makefile
+ # Standard setting: Test the current beta release of Sage:
+ SAGE_REPO: sagemath/sage
+ SAGE_REF: develop
+ # Test with the branch from https://trac.sagemath.org/ticket/33288
+ # This may provide hotfixes for the CI that have not been merged into
+ # the sage develop branch yet.
+ SAGE_TRAC_GIT: https://github.com/sagemath/sagetrac-mirror.git
+ SAGE_TICKET: 33288
+ REMOVE_PATCHES: "*"
+
+jobs:
+
+ dist:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out ${{ env.SPKG }}
+ uses: actions/checkout@v2
+ with:
+ path: build/pkgs/${{ env.SPKG }}/src
+ - name: Install prerequisites
+ run: |
+ sudo DEBIAN_FRONTEND=noninteractive apt-get update
+ sudo DEBIAN_FRONTEND=noninteractive apt-get install $DIST_PREREQ
+ python3 -m pip install build
+ - name: Run make dist, prepare upstream artifact
+ run: |
+ (cd build/pkgs/${{ env.SPKG }}/src && python3 -m bootstrap && python3 -m build --sdist) \
+ && mkdir -p upstream && cp build/pkgs/${{ env.SPKG }}/src/dist/*.tar.gz upstream/${{ env.SPKG }}-git.tar.gz \
+ && echo "sage-package create ${{ env.SPKG }} --version git --tarball ${{ env.SPKG }}-git.tar.gz --type=standard" > upstream/update-pkgs.sh \
+ && if [ -n "${{ env.REMOVE_PATCHES }}" ]; then echo "(cd ../build/pkgs/${{ env.SPKG }}/patches && rm -f ${{ env.REMOVE_PATCHES }}; :)" >> upstream/update-pkgs.sh; fi \
+ && ls -l upstream/
+ - uses: actions/upload-artifact@v2
+ with:
+ path: upstream
+ name: upstream
+
+ docker:
+ runs-on: ubuntu-latest
+ needs: [dist]
+ strategy:
+ fail-fast: false
+ max-parallel: 32
+ matrix:
+ tox_system_factor: [ubuntu-trusty, ubuntu-xenial, ubuntu-bionic, ubuntu-focal, ubuntu-hirsute, ubuntu-impish, ubuntu-jammy, debian-stretch, debian-buster, debian-bullseye, debian-bookworm, debian-sid, linuxmint-17, linuxmint-18, linuxmint-19, linuxmint-19.3, linuxmint-20.1, linuxmint-20.2, linuxmint-20.3, fedora-26, fedora-27, fedora-28, fedora-29, fedora-30, fedora-31, fedora-32, fedora-33, fedora-34, fedora-35, centos-7, centos-stream-8, centos-stream-9, gentoo-python3.9, archlinux-latest, opensuse-15, opensuse-15.3, opensuse-tumbleweed, slackware-14.2, ubuntu-bionic-i386, manylinux-2_24-i686, debian-buster-i386, centos-7-i386]
+ tox_packages_factor: [minimal, standard]
+ env:
+ TOX_ENV: docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}
+ LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-docker-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}
+ DOCKER_TARGETS: configured with-targets with-targets-optional
+ steps:
+ - name: Check out SageMath
+ uses: actions/checkout@v2
+ with:
+ repository: ${{ env.SAGE_REPO }}
+ ref: ${{ env.SAGE_REF }}
+ fetch-depth: 2000
+ if: env.SAGE_REPO != ''
+ - name: Check out git-trac-command
+ uses: actions/checkout@v2
+ with:
+ repository: sagemath/git-trac-command
+ path: git-trac-command
+ if: env.SAGE_TRAC_GIT != ''
+ - name: Check out SageMath from trac.sagemath.org
+ shell: bash {0}
+ run: |
+ git config --global user.email "ci-sage@example.com"
+ git config --global user.name "ci-sage workflow"
+ if [ ! -d .git ]; then git init; fi; git remote add trac ${{ env.SAGE_TRAC_GIT }} && x=1 && while [ $x -le 5 ]; do x=$(( $x + 1 )); sleep $(( $RANDOM % 60 + 1 )); if git-trac-command/git-trac fetch $SAGE_TICKET; then git merge FETCH_HEAD || echo "(ignored)"; exit 0; fi; sleep 40; done; exit 1
+ if: env.SAGE_TRAC_GIT != ''
+ - uses: actions/download-artifact@v2
+ with:
+ path: upstream
+ name: upstream
+ - name: Install test prerequisites
+ run: |
+ sudo DEBIAN_FRONTEND=noninteractive apt-get update
+ sudo DEBIAN_FRONTEND=noninteractive apt-get install tox python3-setuptools
+ - name: Update Sage packages from upstream artifact
+ run: |
+ (export PATH=$(pwd)/build/bin:$PATH; (cd upstream && bash -x update-pkgs.sh) && sed -i.bak '/upstream/d' .dockerignore && echo "/:toolchain:/i ADD upstream upstream" | sed -i.bak -f - build/bin/write-dockerfile.sh && git diff)
+ - name: Configure and build Sage distribution within a Docker container
+ run: |
+ set -o pipefail; EXTRA_DOCKER_BUILD_ARGS="--build-arg USE_MAKEFLAGS=\"-k V=0 SAGE_NUM_THREADS=3\"" tox -e $TOX_ENV -- $TARGETS 2>&1 | sed "/^configure: notice:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: warning:/s|^|::warning file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;/^configure: error:/s|^|::error file=artifacts/$LOGS_ARTIFACT_NAME/config.log::|;"
+ - name: Copy logs from the Docker image or build container
+ run: |
+ mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"
+ cp -r .tox/$TOX_ENV/Dockerfile .tox/$TOX_ENV/log "artifacts/$LOGS_ARTIFACT_NAME"
+ if [ -f .tox/$TOX_ENV/Dockertags ]; then CONTAINERS=$(docker create $(tail -1 .tox/$TOX_ENV/Dockertags) /bin/bash || true); fi
+ if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi
+ if: always()
+ - uses: actions/upload-artifact@v2
+ with:
+ path: artifacts
+ name: ${{ env.LOGS_ARTIFACT_NAME }}
+ if: always()
+ - name: Print out logs for immediate inspection
+ # and markup the output with GitHub Actions logging commands
+ run: |
+ .github/workflows/scan-logs.sh "artifacts/$LOGS_ARTIFACT_NAME"
+ if: always()
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 265be849..821cf883 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -1,6 +1,6 @@
name: tests
-on: [push, pull_request]
+on: [push, pull_request, workflow_dispatch]
jobs:
test:
@@ -10,8 +10,9 @@ jobs:
- stdlib
- local
python:
- - pypy3
- - 3.6
+ - pypy-3.7
+ - 3.7
+ - 3.8
- 3.9
- "3.10"
platform:
@@ -40,8 +41,73 @@ jobs:
${{ runner.os }},
${{ matrix.python }}
- release:
+ test_cygwin:
+ strategy:
+ matrix:
+ distutils:
+ - stdlib
+ - local
+ runs-on: windows-latest
+ env:
+ SETUPTOOLS_USE_DISTUTILS: ${{ matrix.distutils }}
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install Cygwin with Python
+ uses: cygwin/cygwin-install-action@v1
+ with:
+ platform: x86_64
+ packages: >-
+ git,
+ gcc-core,
+ python38,
+ python38-devel,
+ python38-pip
+ - name: Install tox
+ shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}
+ run: |
+ python3.8 -m pip install tox
+ - name: Run tests
+ shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}
+ run: |
+ tox -- --cov-report xml
+
+ integration-test:
+ strategy:
+ matrix:
+ distutils:
+ - stdlib
+ - local
needs: test
+ if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && contains(github.ref, 'refs/tags/'))
+ # To avoid long times and high resource usage, we assume that:
+ # 1. The setuptools APIs used by packages don't vary too much with OS or
+ # Python implementation
+ # 2. Any circumstance for which the previous assumption is not valid is
+ # already tested via unit tests (or other tests not classified here as
+ # "integration")
+ # With that in mind, the integration tests can run for a single setup
+ runs-on: ubuntu-latest
+ env:
+ SETUPTOOLS_USE_DISTUTILS: ${{ matrix.distutils }}
+ steps:
+ - uses: actions/checkout@v2
+ - name: Install OS-level dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install build-essential gfortran libopenblas-dev
+ - name: Setup Python
+ uses: actions/setup-python@v2
+ with:
+ # Use a release that is not very new but still have a long life:
+ python-version: "3.8"
+ - name: Install tox
+ run: |
+ python -m pip install tox
+ - name: Run integration tests
+ run: tox -e integration
+
+ release:
+ needs: [test, test_cygwin, integration-test]
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index dc14826e..90ae8050 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,7 +6,6 @@ docs/build
include
lib
distribute.egg-info
-foo.egg-info
setuptools.egg-info
.coverage
.eggs
diff --git a/CHANGES.rst b/CHANGES.rst
index 2ba54410..cd46ace6 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -1,3 +1,372 @@
+v60.8.2
+-------
+
+
+Misc
+^^^^
+* #3091: Make ``concurrent.futures`` import lazy in vendored ``more_itertools``
+ package to a avoid importing threading as a side effect (which caused
+ `gevent/gevent#1865 <https://github.com/gevent/gevent/issues/1865>`__).
+ -- by :user:`maciejp-ro`
+
+
+v60.8.1
+-------
+
+
+Misc
+^^^^
+* #3084: When vendoring jaraco packages, ensure the namespace package is converted to a simple package to support zip importer.
+
+
+v60.8.0
+-------
+
+
+Changes
+^^^^^^^
+* #3085: Setuptools now vendors importlib_resources and importlib_metadata and jaraco.text. Setuptools no longer relies on pkg_resources for ensure_directory nor parse_requirements.
+
+
+v60.7.1
+-------
+
+
+Misc
+^^^^
+* #3072: Remove lorem_ipsum from jaraco.text when vendored.
+
+
+v60.7.0
+-------
+
+
+Changes
+^^^^^^^
+* #3061: Vendored jaraco.text and use line processing from that library in pkg_resources.
+
+Misc
+^^^^
+* #3070: Avoid AttributeError in easy_install.create_home_path when sysconfig.get_config_vars values are not strings.
+
+
+v60.6.0
+-------
+
+
+Changes
+^^^^^^^
+* #3043: Merge with pypa/distutils@bb018f1ac3 including consolidated behavior in sysconfig.get_platform (pypa/distutils#104).
+* #3057: Don't include optional ``Home-page`` in metadata if no ``url`` is specified. -- by :user:`cdce8p`
+* #3062: Merge with pypa/distutils@b53a824ec3 including improved support for lib directories on non-x64 Windows builds.
+
+Documentation changes
+^^^^^^^^^^^^^^^^^^^^^
+* #2897: Added documentation about wrapping ``setuptools.build_meta`` in a in-tree
+ custom backend. This is a :pep:`517`-compliant way of dynamically specifying
+ build dependencies (e.g. when platform, OS and other markers are not enough).
+ -- by :user:`abravalheri`
+* #3034: Replaced occurrences of the defunct distutils-sig mailing list with pointers
+ to GitHub Discussions.
+ -- by :user:`ashemedai`
+* #3056: The documentation has stopped suggesting to add ``wheel`` to
+ :pep:`517` requirements -- by :user:`webknjaz`
+
+Misc
+^^^^
+* #3054: Used Py3 syntax ``super().__init__()`` -- by :user:`imba-tjd`
+
+
+v60.5.4
+-------
+
+
+Misc
+^^^^
+* #3009: Remove filtering of distutils warnings.
+* #3031: Suppress distutils replacement when building or testing CPython.
+
+
+v60.5.3
+-------
+
+
+Misc
+^^^^
+* #3026: Honor sysconfig variables in easy_install.
+
+
+v60.5.2
+-------
+
+
+Misc
+^^^^
+* #2993: In _distutils_hack, for get-pip, simulate existence of setuptools.
+
+
+v60.5.1
+-------
+
+
+Misc
+^^^^
+* #2918: Correct support for Python 3 native loaders.
+
+
+v60.5.0
+-------
+
+
+Changes
+^^^^^^^
+* #2990: Set the ``.origin`` attribute of the ``distutils`` module to the module's ``__file__``.
+
+
+v60.4.0
+-------
+
+
+Changes
+^^^^^^^
+* #2839: Removed `requires` sorting when installing wheels as an egg dir.
+* #2953: Fixed a bug that easy install incorrectly parsed Python 3.10 version string.
+* #3006: Fixed startup performance issue of Python interpreter due to imports of
+ costly modules in ``_distutils_hack`` -- by :user:`tiran`
+
+Documentation changes
+^^^^^^^^^^^^^^^^^^^^^
+* #2674: Added link to additional resources on packaging in Quickstart guide
+* #3008: "In-tree" Sphinx extension for "favicons" replaced with ``sphinx-favicon``.
+* #3008: SVG images (logo, banners, ...) optimised with the help of the ``scour``
+ package.
+
+Misc
+^^^^
+* #2862: Added integration tests that focus on building and installing some packages in
+ the Python ecosystem via ``pip`` -- by :user:`abravalheri`
+* #2952: Modified "vendoring" logic to keep license files.
+* #2968: Improved isolation for some tests that where inadvertently using the project
+ root for builds, and therefore creating directories (e.g. ``build``, ``dist``,
+ ``*.egg-info``) that could interfere with the outcome of other tests
+ -- by :user:`abravalheri`.
+* #2968: Introduced new test fixtures ``venv``, ``venv_without_setuptools``,
+ ``bare_venv`` that rely on the ``jaraco.envs`` package.
+ These new test fixtures were also used to remove the (currently problematic)
+ dependency on the ``pytest_virtualenv`` plugin.
+* #2968: Removed ``tmp_src`` test fixture. Previously this fixture was copying all the
+ files and folders under the project root, including the ``.git`` directory,
+ which is error prone and increases testing time.
+
+ Since ``tmp_src`` was used to populate virtual environments (installing the
+ version of ``setuptools`` under test via the source tree), it was replaced by
+ the new ``setuptools_sdist`` and ``setuptools_wheel`` fixtures (that are build
+ only once per session testing and can be shared between all the workers for
+ read-only usage).
+
+
+v60.3.1
+-------
+
+
+Misc
+^^^^
+* #3002: Suppress AttributeError when detecting get-pip.
+
+
+v60.3.0
+-------
+
+
+Changes
+^^^^^^^
+* #2993: In _distutils_hack, bypass the distutils exception for pip when get-pip is being invoked, because it imports setuptools.
+
+Misc
+^^^^
+* #2989: Merge with pypa/distutils@788cc159. Includes fix for config vars missing from sysconfig.
+
+
+v60.2.0
+-------
+
+
+Changes
+^^^^^^^
+* #2974: Setuptools now relies on the Python logging infrastructure to log messages. Instead of using ``distutils.log.*``, use ``logging.getLogger(name).*``.
+* #2987: Sync with pypa/distutils@2def21c5d74fdd2fe7996ee4030ac145a9d751bd, including fix for missing get_versions attribute (#2969), more reliance on sysconfig from stdlib.
+
+Misc
+^^^^
+* #2962: Avoid attempting to use local distutils when the presiding version of Setuptools on the path doesn't have one.
+* #2983: Restore 'add_shim' as the way to invoke the hook. Avoids compatibility issues between different versions of Setuptools with the distutils local implementation.
+
+
+v60.1.1
+-------
+
+
+Misc
+^^^^
+* #2980: Bypass distutils loader when setuptools module is no longer available on sys.path.
+
+
+v60.1.0
+-------
+
+
+Changes
+^^^^^^^
+* #2958: In distutils_hack, only add the metadata finder once. In ensure_local_distutils, rely on a context manager for reliable manipulation.
+* #2963: Merge with pypa/distutils@a5af364910. Includes revisited fix for pypa/distutils#15 and improved MinGW/Cygwin support from pypa/distutils#77.
+
+
+v60.0.5
+-------
+
+
+Misc
+^^^^
+* #2960: Install schemes fall back to default scheme for headers.
+
+
+v60.0.4
+-------
+
+
+Misc
+^^^^
+* #2954: Merge with pypa/distutils@eba2bcd310. Adds platsubdir to config vars available for substitution.
+
+
+v60.0.3
+-------
+
+
+Misc
+^^^^
+* #2940: Avoid KeyError in distutils hack when pip is imported during ensurepip.
+
+
+v60.0.2
+-------
+
+
+Misc
+^^^^
+* #2938: Select 'posix_user' for the scheme unless falling back to stdlib, then use 'unix_user'.
+
+
+v60.0.1
+-------
+
+
+Misc
+^^^^
+* #2944: Add support for extended install schemes in easy_install.
+
+
+v60.0.0
+-------
+
+
+Breaking Changes
+^^^^^^^^^^^^^^^^
+* #2896: Setuptools once again makes its local copy of distutils the default. To override, set SETUPTOOLS_USE_DISTUTILS=stdlib.
+
+
+v59.8.0
+-------
+
+
+Changes
+^^^^^^^
+* #2935: Merge pypa/distutils@460b59f0e68dba17e2465e8dd421bbc14b994d1f.
+
+
+v59.7.0
+-------
+
+
+Changes
+^^^^^^^
+* #2930: Require Python 3.7
+
+
+v59.6.0
+-------
+
+
+Changes
+^^^^^^^
+* #2925: Merge with pypa/distutils@92082ee42c including introduction of deprecation warning on Version classes.
+
+
+v59.5.0
+-------
+
+
+Changes
+^^^^^^^
+* #2914: Merge with pypa/distutils@8f2df0bf6.
+
+
+v59.4.0
+-------
+
+
+Changes
+^^^^^^^
+* #2893: Restore deprecated support for newlines in the Summary field.
+
+
+v59.3.0
+-------
+
+
+Changes
+^^^^^^^
+* #2902: Merge with pypa/distutils@85db7a41242.
+
+Misc
+^^^^
+* #2906: In ensure_local_distutils, re-use DistutilsMetaFinder to load the module. Avoids race conditions when _distutils_system_mod is employed.
+
+
+v59.2.0
+-------
+
+
+Changes
+^^^^^^^
+* #2875: Introduce changes from pypa/distutils@514e9d0, including support for overrides from Debian and pkgsrc, unlocking the possibility of making SETUPTOOLS_USE_DISTUTILS=local the default again.
+
+
+v59.1.1
+-------
+
+
+Misc
+^^^^
+* #2885: Fixed errors when encountering LegacyVersions.
+
+
+v59.1.0
+-------
+
+
+Changes
+^^^^^^^
+* #2497: Update packaging to 21.2.
+* #2877: Back out deprecation of setup_requires and replace instead by a deprecation of setuptools.installer and fetch_build_egg. Now setup_requires is still supported when installed as part of a PEP 517 build, but is deprecated when an unsatisfied requirement is encountered.
+* #2879: Bump packaging to 21.2.
+
+Documentation changes
+^^^^^^^^^^^^^^^^^^^^^
+* #2867: PNG/ICO images replaced with SVG in the docs.
+* #2867: Added support to SVG "favicons" via "in-tree" Sphinx extension.
+
+
v59.0.1
-------
@@ -2248,7 +2617,7 @@ v30.3.0
* #394 via #862: Added support for `declarative package
config in a setup.cfg file
- <https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_.
+ <https://setuptools.pypa.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_.
v30.2.1
-------
@@ -2709,7 +3078,7 @@ v23.0.0
about, please comment in the ticket.
* #604: Removed docs building support. The project
now relies on documentation hosted at
- https://setuptools.readthedocs.io/.
+ https://setuptools.pypa.io/.
v22.0.5
-------
@@ -2859,7 +3228,7 @@ v20.6.0
`semver <https://semver.org>`_ precisely.
The 'v' prefix on version numbers now also allows
version numbers to be referenced in the changelog,
- e.g. http://setuptools.readthedocs.io/en/latest/history.html#v20-6-0.
+ e.g. http://setuptools.pypa.io/en/latest/history.html#v20-6-0.
20.5
----
@@ -2939,7 +3308,7 @@ v20.6.0
* Added support for using passwords from keyring in the upload
command. See `the upload docs
- <https://setuptools.readthedocs.io/en/latest/setuptools.html#upload-upload-source-and-or-egg-distributions-to-pypi>`_
+ <https://setuptools.pypa.io/en/latest/setuptools.html#upload-upload-source-and-or-egg-distributions-to-pypi>`_
for details.
20.0
@@ -3693,7 +4062,7 @@ process to fail and PyPI uploads no longer accept files for 13.0.
---
* Added a `Developer Guide
- <https://setuptools.readthedocs.io/en/latest/developer-guide.html>`_ to the official
+ <https://setuptools.pypa.io/en/latest/developer-guide.html>`_ to the official
documentation.
* Some code refactoring and cleanup was done with no intended behavioral
changes.
@@ -5400,7 +5769,7 @@ easy_install
* ``setuptools`` now finds its commands, ``setup()`` argument validators, and
metadata writers using entry points, so that they can be extended by
third-party packages. See `Creating distutils Extensions
- <https://setuptools.readthedocs.io/en/latest/setuptools.html#creating-distutils-extensions>`_
+ <https://setuptools.pypa.io/en/latest/setuptools.html#creating-distutils-extensions>`_
for more details.
* The vestigial ``depends`` command has been removed. It was never finished
diff --git a/README.rst b/README.rst
index 6e7b95cf..e421de51 100644
--- a/README.rst
+++ b/README.rst
@@ -20,9 +20,9 @@
:alt: Code style: Black
.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg
- :target: https://setuptools.readthedocs.io
+ :target: https://setuptools.pypa.io
-.. image:: https://img.shields.io/badge/skeleton-2021-informational
+.. image:: https://img.shields.io/badge/skeleton-2022-informational
:target: https://blog.jaraco.com/skeleton
.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white
@@ -31,13 +31,17 @@
.. 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 `Installation Instructions
<https://packaging.python.org/installing/>`_ in the Python Packaging
User's Guide for instructions on installing, upgrading, and uninstalling
Setuptools.
-Questions and comments should be directed to the `distutils-sig
-mailing list <http://mail.python.org/pipermail/distutils-sig/>`_.
+Questions and comments should be directed to `GitHub Discussions
+<https://github.com/pypa/setuptools/discussions>`_.
Bug reports and especially tested patches may be
submitted directly to the `bug tracker
<https://github.com/pypa/setuptools/issues>`_.
@@ -47,7 +51,7 @@ Code of Conduct
===============
Everyone interacting in the setuptools project's codebases, issue trackers,
-chat rooms, and mailing lists is expected to follow the
+chat rooms, and fora is expected to follow the
`PSF Code of Conduct <https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md>`_.
diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py
index 5f40996a..1f8daf49 100644
--- a/_distutils_hack/__init__.py
+++ b/_distutils_hack/__init__.py
@@ -1,18 +1,11 @@
+# don't import any costly modules
import sys
import os
-import re
-import importlib
-import warnings
is_pypy = '__pypy__' in sys.builtin_module_names
-warnings.filterwarnings('ignore',
- r'.+ distutils\b.+ deprecated',
- DeprecationWarning)
-
-
def warn_distutils_present():
if 'distutils' not in sys.modules:
return
@@ -20,6 +13,7 @@ def warn_distutils_present():
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
return
+ import warnings
warnings.warn(
"Distutils was imported before Setuptools, but importing Setuptools "
"also replaces the `distutils` module in `sys.modules`. This may lead "
@@ -32,8 +26,12 @@ def warn_distutils_present():
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 re.match(r'distutils\b', name)]
+ mods = [
+ name for name in sys.modules
+ if name == "distutils" or name.startswith("distutils.")
+ ]
for name in mods:
del sys.modules[name]
@@ -42,17 +40,21 @@ def enabled():
"""
Allow selection of distutils by environment variable.
"""
- which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib')
+ which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
return which == 'local'
def ensure_local_distutils():
+ import importlib
clear_distutils()
- distutils = importlib.import_module('setuptools._distutils')
- distutils.__name__ = 'distutils'
- sys.modules['distutils'] = distutils
- # sanity check that submodules load as expected
+ # 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__
@@ -69,6 +71,14 @@ def do_override():
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):
if path is not None:
@@ -79,18 +89,45 @@ class DistutilsMetaFinder:
return method()
def spec_for_distutils(self):
+ if self.is_cpython():
+ return
+
+ 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
+
class DistutilsLoader(importlib.abc.Loader):
def create_module(self, spec):
- return importlib.import_module('setuptools._distutils')
+ return mod
def exec_module(self, module):
pass
- return importlib.util.spec_from_loader('distutils', DistutilsLoader())
+ 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):
"""
@@ -102,22 +139,77 @@ class DistutilsMetaFinder:
clear_distutils()
self.spec_for_distutils = lambda: None
- @staticmethod
- def pip_imported_during_build():
+ def spec_for_setuptools(self):
+ """
+ get-pip imports setuptools solely for the purpose of
+ determining if it's installed. In this case, provide
+ a stubbed spec to represent setuptools being present
+ without invoking any behavior.
+
+ Workaround for pypa/get-pip#137. Ref #2993.
+ """
+ if not self.is_script('get-pip'):
+ return
+
+ import importlib
+
+ class StubbedLoader(importlib.abc.Loader):
+
+ def create_module(self, spec):
+ import types
+ return types.ModuleType('setuptools')
+
+ def exec_module(self, module):
+ pass
+
+ return importlib.util.spec_from_loader(
+ 'setuptools', StubbedLoader(),
+ )
+
+ @classmethod
+ def pip_imported_during_build(cls):
"""
Detect if pip is being imported in a build script. Ref #2355.
"""
import traceback
return any(
- frame.f_globals['__file__'].endswith('setup.py')
+ cls.frame_file_is_setup(frame)
for frame, line in traceback.walk_stack(None)
)
+ @staticmethod
+ def is_script(name):
+ try:
+ import __main__
+ return os.path.basename(__main__.__file__) == f'{name}.py'
+ except AttributeError:
+ pass
+
+ @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')
+
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)
diff --git a/bootstrap.py b/bootstrap.py
deleted file mode 100644
index 229b9965..00000000
--- a/bootstrap.py
+++ /dev/null
@@ -1,7 +0,0 @@
-import warnings
-
-
-msg = "bootstrap.py is no longer needed. Use a PEP-517-compatible builder instead."
-
-
-__name__ == '__main__' and warnings.warn(msg)
diff --git a/conftest.py b/conftest.py
index d5e851fe..43f33ba4 100644
--- a/conftest.py
+++ b/conftest.py
@@ -1,5 +1,7 @@
import sys
+import pytest
+
pytest_plugins = 'setuptools.tests.fixtures'
@@ -9,6 +11,14 @@ def pytest_addoption(parser):
"--package_name", action="append", default=[],
help="list of package_name to pass to test functions",
)
+ parser.addoption(
+ "--integration", action="store_true", default=False,
+ help="run integration tests (only)"
+ )
+
+
+def pytest_configure(config):
+ config.addinivalue_line("markers", "integration: integration tests")
collect_ignore = [
@@ -27,3 +37,13 @@ collect_ignore = [
if sys.version_info < (3, 6):
collect_ignore.append('docs/conf.py') # uses f-strings
collect_ignore.append('pavement.py')
+
+
+@pytest.fixture(autouse=True)
+def _skip_integration(request):
+ running_integration_tests = request.config.getoption("--integration")
+ is_integration_test = request.node.get_closest_marker("integration")
+ if running_integration_tests and not is_integration_test:
+ pytest.skip("running integration tests only")
+ if not running_integration_tests and is_integration_test:
+ pytest.skip("skipping integration tests")
diff --git a/docs/images/README.rst b/docs/artwork.rst
index d80dc965..907e62a6 100644
--- a/docs/images/README.rst
+++ b/docs/artwork.rst
@@ -2,7 +2,7 @@
Artwork
=======
-.. figure:: logo-over-white.svg
+.. figure:: images/logo-over-white.svg
:align: center
Setuptools logo, designed in 2021 by `Anderson Bravalheri`_
@@ -35,7 +35,7 @@ on the circumstances:
The following image illustrate these alternatives:
-.. image:: logo-demo-editable-inkscape.png
+.. image:: images/logo-demo.svg
:align: center
Please refer to the SVG files in the `setuptools repository`_ for the specific
@@ -107,7 +107,7 @@ used by the setuptools software (MIT):
SETUPTOOLS PROJECT.
Whenever possible, please make the image a link to
-https://github.com/pypa/setuptools.
+https://github.com/pypa/setuptools or https://setuptools.pypa.io.
.. _Anderson Bravalheri: https://github.com/abravalheri
diff --git a/docs/build_meta.rst b/docs/build_meta.rst
index 27df70a2..1337bddb 100644
--- a/docs/build_meta.rst
+++ b/docs/build_meta.rst
@@ -9,29 +9,29 @@ Python packaging has come `a long way <https://bernat.tech/posts/pep-517-518/>`_
The traditional ``setuptools`` way of packaging Python modules
uses a ``setup()`` function within the ``setup.py`` script. Commands such as
-``python setup.py bdist`` or ``python setup.py bdist_wheel`` generate a
-distribution bundle and ``python setup.py install`` installs the distribution.
-This interface makes it difficult to choose other packaging tools without an
+``python setup.py bdist`` or ``python setup.py bdist_wheel`` generate a
+distribution bundle and ``python setup.py install`` installs the distribution.
+This interface makes it difficult to choose other packaging tools without an
overhaul. Because ``setup.py`` scripts allowed for arbitrary execution, it
proved difficult to provide a reliable user experience across environments
and history.
`PEP 517 <https://www.python.org/dev/peps/pep-0517/>`_ therefore came to
-rescue and specified a new standard to
+rescue and specified a new standard to
package and distribute Python modules. Under PEP 517:
a ``pyproject.toml`` file is used to specify what program to use
- for generating distribution.
+ for generating distribution.
- Then, two functions provided by the program, ``build_wheel(directory: str)``
- and ``build_sdist(directory: str)`` create the distribution bundle at the
- specified ``directory``. The program is free to use its own configuration
- script or extend the ``.toml`` file.
+ Then, two functions provided by the program, ``build_wheel(directory: str)``
+ and ``build_sdist(directory: str)`` create the distribution bundle at the
+ specified ``directory``. The program is free to use its own configuration
+ script or extend the ``.toml`` file.
Lastly, ``pip install *.whl`` or ``pip install *.tar.gz`` does the actual
installation. If ``*.whl`` is available, ``pip`` will go ahead and copy
the files into ``site-packages`` directory. If not, ``pip`` will look at
- ``pyproject.toml`` and decide what program to use to 'build from source'
+ ``pyproject.toml`` and decide what program to use to 'build from source'
(the default is ``setuptools``)
With this standard, switching between packaging tools becomes a lot easier. ``build_meta``
@@ -48,17 +48,18 @@ scripts, a ``pyproject.toml`` file and a ``setup.cfg`` file::
setup.cfg
meowpkg/__init__.py
-The pyproject.toml file is required to specify the build system (i.e. what is
-being used to package your scripts and install from source). To use it with
+The pyproject.toml file is required to specify the build system (i.e. what is
+being used to package your scripts and install from source). To use it with
setuptools, the content would be::
[build-system]
- requires = ["setuptools", "wheel"]
+ requires = ["setuptools"]
build-backend = "setuptools.build_meta"
The ``setuptools`` package implements the ``build_sdist``
command and the ``wheel`` package implements the ``build_wheel``
-command; both are required to be compliant with PEP 517.
+command; the latter is a dependency of the former
+exposed via :pep:`517` hooks.
Use ``setuptools``' :ref:`declarative config <declarative config>` to
specify the package information::
@@ -67,7 +68,7 @@ specify the package information::
name = meowpkg
version = 0.0.1
description = a package that meows
-
+
[options]
packages = find:
@@ -77,7 +78,7 @@ Now generate the distribution. To build the package, use
$ pip install -q build
$ python -m build
-And now it's done! The ``.whl`` file and ``.tar.gz`` can then be distributed
+And now it's done! The ``.whl`` file and ``.tar.gz`` can then be distributed
and installed::
dist/
@@ -89,3 +90,85 @@ and installed::
or::
$ pip install dist/meowpkg-0.0.1.tar.gz
+
+Dynamic build dependencies and other ``build_meta`` tweaks
+----------------------------------------------------------
+
+With the changes introduced by :pep:`517` and :pep:`518`, the
+``setup_requires`` configuration field was made deprecated in ``setup.cfg`` and
+``setup.py``, in favour of directly listing build dependencies in the
+``requires`` field of the ``build-system`` table of ``pyproject.toml``.
+This approach has a series of advantages and gives package managers and
+installers the ability to inspect in advance the build requirements and
+perform a series of optimisations.
+
+However some package authors might still need to dynamically inspect the final
+users machine before deciding these requirements. One way of doing that, as
+specified by :pep:`517`, is to "tweak" ``setuptools.build_meta`` by using a
+:pep:`in-tree backend <517#in-tree-build-backends>`.
+
+.. tip:: Before implementing a *in-tree* backend, have a look on
+ :pep:`PEP 508 <508#environment-markers>`. Most of the times, dependencies
+ with **environment markers** are enough to differentiate operating systems
+ and platforms.
+
+If you add the following configuration to your ``pyprojec.toml``:
+
+
+.. code-block:: toml
+
+ [build-system]
+ requires = ["setuptools", "wheel"]
+ build-backend = "backend"
+ backend-path = ["_custom_build"]
+
+
+then you should be able to implement a thin wrapper around ``build_meta`` in
+the ``_custom_build/backend.py`` file, as shown in the following example:
+
+.. code-block:: python
+
+ from setuptools import build_meta as _orig
+
+ prepare_metadata_for_build_wheel = _orig.prepare_metadata_for_build_wheel
+ build_wheel = _orig.build_wheel
+ build_sdist = _orig.build_sdist
+
+
+ def get_requires_for_build_wheel(self, config_settings=None):
+ return _orig.get_requires_for_build_wheel(config_settings) + [...]
+
+
+ def get_requires_for_build_sdist(self, config_settings=None):
+ return _orig.get_requires_for_build_sdist(config_settings) + [...]
+
+
+Note that you can override any of the functions specified in :pep:`PEP 517
+<517#build-backend-interface>`, not only the ones responsible for gathering
+requirements.
+
+.. important:: Make sure your backend script is included in the :doc:`source
+ distribution </userguide/distribution>`, otherwise the build will fail.
+ This can be done by using a SCM_/VCS_ plugin (like :pypi:`setuptools-scm`
+ and :pypi:`setuptools-svn`), or by correctly setting up :ref:`MANIFEST.in
+ <manifest>`.
+
+ If this is the first time you are using a customised backend, please have a
+ look on the generated ``.tar.gz`` and ``.whl``.
+ On POSIX systems that can be done with ``tar -tf dist/*.tar.gz``
+ and ``unzip -l dist/*.whl``.
+ On Windows systems you can rename the ``.whl`` to ``.zip`` to be able to
+ inspect it on the file explorer, and use the same ``tar`` command in a
+ command prompt (alternativelly there are GUI programs like `7-zip`_ that
+ handle ``.tar.gz``).
+
+ In general the backend script should be present in the ``.tar.gz`` (so the
+ project can be build from the source) but not in the ``.whl`` (otherwise the
+ backend script would end up being distributed alongside your package).
+ See ":doc:`/userguide/package_discovery`" for more details about package
+ files.
+
+
+.. _SCM: https://en.wikipedia.org/wiki/Software_configuration_management
+.. _VCS: https://en.wikipedia.org/wiki/Version_control
+.. _7-zip: https://www.7-zip.org
diff --git a/docs/conf.py b/docs/conf.py
index d352a481..bfd45a69 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -66,6 +66,10 @@ link_files = {
url='{GH}/pypa/distutils/issues/{distutils}',
),
dict(
+ pattern=r'pypa/distutils@(?P<distutils_commit>[\da-f]+)',
+ url='{GH}/pypa/distutils/commit/{distutils_commit}',
+ ),
+ dict(
pattern=r'^(?m)((?P<scm_version>v?\d+(\.\d+){1,2}))\n[-=]+\n',
with_scm='{text}\n{rev[timestamp]:%d %b %Y}\n',
),
@@ -92,6 +96,7 @@ github_url = 'https://github.com'
github_sponsors_url = f'{github_url}/sponsors'
extlinks = {
'user': (f'{github_sponsors_url}/%s', '@'), # noqa: WPS323
+ 'pypi': ('https://pypi.org/project/%s', '%s'),
}
extensions += ['sphinx.ext.extlinks']
@@ -101,8 +106,7 @@ default_role = 'any'
# HTML theme
html_theme = 'furo'
-html_logo = "images/logo.png"
-html_favicon = "images/favicon.ico"
+html_logo = "images/logo.svg"
html_theme_options = {
"sidebar_hide_name": True,
@@ -172,4 +176,26 @@ towncrier_draft_include_empty = False
extensions += ['jaraco.tidelift']
+# Add icons (aka "favicons") to documentation
+extensions += ['sphinx-favicon']
+html_static_path = ['images'] # should contain the folder with icons
+
+# List of dicts with <link> HTML attributes
+# static-file points to files in the html_static_path (href is computed)
+favicons = [
+ { # "Catch-all" goes first, otherwise some browsers will overwrite
+ "rel": "icon",
+ "type": "image/svg+xml",
+ "static-file": "logo-symbol-only.svg",
+ "sizes": "any"
+ },
+ { # Version with thicker strokes for better visibility at smaller sizes
+ "rel": "icon",
+ "type": "image/svg+xml",
+ "static-file": "favicon.svg",
+ "sizes": "16x16 24x24 32x32 48x48"
+ },
+ # rel="apple-touch-icon" does not support SVG yet
+]
+
intersphinx_mapping['pip'] = 'https://pip.pypa.io/en/latest', None
diff --git a/docs/deprecated/distutils-legacy.rst b/docs/deprecated/distutils-legacy.rst
index 94104fe8..148dc259 100644
--- a/docs/deprecated/distutils-legacy.rst
+++ b/docs/deprecated/distutils-legacy.rst
@@ -5,7 +5,7 @@ Setuptools and the PyPA have a `stated goal <https://github.com/pypa/packaging-p
Since the 49.1.2 release, Setuptools includes a local, vendored copy of distutils (from late copies of CPython) that is disabled by default. To enable the use of this copy of distutils when invoking setuptools, set the enviroment variable:
- SETUPTOOLS_USE_DISTUTILS=local
+ SETUPTOOLS_USE_DISTUTILS=local
This behavior is planned to become the default.
diff --git a/docs/deprecated/distutils/_setuptools_disclaimer.rst b/docs/deprecated/distutils/_setuptools_disclaimer.rst
index cc758583..628c2e4f 100644
--- a/docs/deprecated/distutils/_setuptools_disclaimer.rst
+++ b/docs/deprecated/distutils/_setuptools_disclaimer.rst
@@ -1,5 +1,5 @@
.. note::
This document is being retained solely until the ``setuptools`` documentation
- at https://setuptools.readthedocs.io/en/latest/setuptools.html
+ at https://setuptools.pypa.io/en/latest/setuptools.html
independently covers all of the relevant information currently included here.
diff --git a/docs/deprecated/distutils/apiref.rst b/docs/deprecated/distutils/apiref.rst
index f721fc16..f00ed74c 100644
--- a/docs/deprecated/distutils/apiref.rst
+++ b/docs/deprecated/distutils/apiref.rst
@@ -11,7 +11,7 @@ API Reference
and other APIs, makes the API consistent across different Python versions,
and is hence recommended over using ``distutils`` directly.
-.. _New and changed setup.py arguments in setuptools: https://setuptools.readthedocs.io/en/latest/setuptools.html#new-and-changed-setup-keywords
+.. _New and changed setup.py arguments in setuptools: https://setuptools.pypa.io/en/latest/setuptools.html#new-and-changed-setup-keywords
.. include:: ./_setuptools_disclaimer.rst
diff --git a/docs/deprecated/functionalities.rst b/docs/deprecated/functionalities.rst
index c6ea83b3..7213c5d6 100644
--- a/docs/deprecated/functionalities.rst
+++ b/docs/deprecated/functionalities.rst
@@ -30,4 +30,4 @@ invoked via symlinks. They *must* be invoked using their original filename, in
order to ensure that, once running, ``pkg_resources`` will know what project
and version is in use. The header script will check this and exit with an
error if the ``.egg`` file has been renamed or is invoked via a symlink that
-changes its base name. \ No newline at end of file
+changes its base name.
diff --git a/docs/development/developer-guide.rst b/docs/development/developer-guide.rst
index 052ca762..d2cf1592 100644
--- a/docs/development/developer-guide.rst
+++ b/docs/development/developer-guide.rst
@@ -25,12 +25,12 @@ Setuptools is maintained primarily in GitHub at `this home
Python Packaging Authority (PyPA) with several core contributors. All bugs
for Setuptools are filed and the canonical source is maintained in GitHub.
-User support and discussions are done through the issue tracker (for specific)
-issues, through the `distutils-sig mailing list <https://mail.python.org/mailman3/lists/distutils-sig.python.org/>`_, or on IRC (Freenode) at
-#pypa.
+User support and discussions are done through
+`GitHub Discussions <https://github.com/pypa/setuptools/discussions>`_,
+or the issue tracker (for specific issues).
-Discussions about development happen on the distutils-sig mailing list or on
-`Gitter <https://gitter.im/pypa/setuptools>`_.
+Discussions about development happen on GitHub Discussions or
+the ``setuptools`` channel on `PyPA Discord <https://discord.com/invite/pypa>`_.
-----------------
Authoring Tickets
@@ -110,7 +110,7 @@ To build the docs locally, use tox::
$ tox -e docs
.. _Sphinx: http://www.sphinx-doc.org/en/master/
-.. _published documentation: https://setuptools.readthedocs.io/en/latest/
+.. _published documentation: https://setuptools.pypa.io/en/latest/
---------------------
Vendored Dependencies
@@ -125,12 +125,9 @@ cannot declare dependencies other than through
``setuptools/_vendor/vendored.txt`` and
``pkg_resources/_vendor/vendored.txt``.
-All the dependencies specified in these files are "vendorized" using Paver_, a
-simple Python-based project scripting and task running tool.
+All the dependencies specified in these files are "vendorized" using a
+simple Python script ``tools/vendor.py``.
-To refresh the dependencies, you can run the following command (defined in
-``pavement.py``)::
+To refresh the dependencies, run the following command::
- $ paver update_vendored
-
-.. _Paver: https://pythonhosted.org/Paver/
+ $ tox -e vendor
diff --git a/docs/images/banner-640x320.png b/docs/images/banner-640x320.png
deleted file mode 100644
index ce7f99f2..00000000
--- a/docs/images/banner-640x320.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/banner-640x320.svg b/docs/images/banner-640x320.svg
index 8222f645..4e908ea1 100644
--- a/docs/images/banner-640x320.svg
+++ b/docs/images/banner-640x320.svg
@@ -1,101 +1,36 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- id="svg1021"
- version="1.1"
- viewBox="0 0 169.33333 84.666662"
- height="320"
- width="640">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="matrix(2.0252536,0,0,2.0252536,-0.20252404,-105.4321)"
- id="g848">
- <g
- id="layer1"
- transform="matrix(0.93437246,0,0,0.93437246,0.32003559,-27.205658)">
- <g
- id="g1751"
- transform="matrix(0.45669594,0,0,0.45669594,-11.041899,60.847347)">
- <path
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- id="rect934" />
- <path
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- id="path944" />
- </g>
- <g
- id="g1747"
- transform="translate(0,-23.386941)">
- <g
- id="text1023"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#336790;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="SETUP">
- <path
- id="path1713"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- <path
- id="path1715"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z" />
- <path
- id="path1717"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1719"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z" />
- <path
- id="path1721"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z" />
- </g>
- <g
- id="text1661"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="TOOLS">
- <path
- id="path1724"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1726"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1728"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1730"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z" />
- <path
- id="path1732"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="640" height="320" version="1.1" viewBox="0 0 169.33 84.667" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="g848" transform="matrix(2.0253 0 0 2.0253 -.20252 -105.43)">
+ <g id="layer1" transform="matrix(.93437 0 0 .93437 .32004 -27.206)">
+ <g id="g1751" transform="matrix(.4567 0 0 .4567 -11.042 60.847)">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#336790" style="paint-order:markers fill stroke"/>
+ </g>
+ <g id="g1747" transform="translate(0 -23.387)" stroke-width=".22458">
+ <g id="text1023" fill="#336790" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
</g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
</g>
+ </g>
</svg>
diff --git a/docs/images/banner-negative-640x320.png b/docs/images/banner-negative-640x320.png
deleted file mode 100644
index c0126f8b..00000000
--- a/docs/images/banner-negative-640x320.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/banner-negative-640x320.svg b/docs/images/banner-negative-640x320.svg
index fd5535fd..d45698ed 100644
--- a/docs/images/banner-negative-640x320.svg
+++ b/docs/images/banner-negative-640x320.svg
@@ -1,109 +1,37 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- id="svg1021"
- version="1.1"
- viewBox="0 0 169.33333 84.666662"
- height="320"
- width="640">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="matrix(2.0252536,0,0,2.0252536,-0.20252404,-105.4321)"
- id="g848">
- <rect
- ry="0"
- y="52.058716"
- x="0.1"
- height="41.805462"
- width="83.610924"
- id="rect830"
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.48264033;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- <g
- id="layer1"
- transform="matrix(0.93437246,0,0,0.93437246,0.32003559,-27.205658)">
- <g
- id="g1751"
- transform="matrix(0.45669594,0,0,0.45669594,-11.041899,60.847347)">
- <path
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- id="rect934" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- id="path944" />
- </g>
- <g
- id="g1747"
- transform="translate(0,-23.386941)">
- <g
- id="text1023"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="SETUP">
- <path
- id="path1713"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- <path
- id="path1715"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z" />
- <path
- id="path1717"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1719"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z" />
- <path
- id="path1721"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z" />
- </g>
- <g
- id="text1661"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="TOOLS">
- <path
- id="path1724"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1726"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1728"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1730"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z" />
- <path
- id="path1732"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="640" height="320" version="1.1" viewBox="0 0 169.33 84.667" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="g848" transform="matrix(2.0253 0 0 2.0253 -.20252 -105.43)">
+ <rect id="rect830" x=".1" y="52.059" width="83.611" height="41.805" ry="0" fill="#336790" style="paint-order:markers fill stroke"/>
+ <g id="layer1" transform="matrix(.93437 0 0 .93437 .32004 -27.206)">
+ <g id="g1751" transform="matrix(.4567 0 0 .4567 -11.042 60.847)">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#fff" style="paint-order:markers fill stroke"/>
+ </g>
+ <g id="g1747" transform="translate(0 -23.387)" stroke-width=".22458">
+ <g id="text1023" fill="#fff" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
</g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
</g>
+ </g>
</svg>
diff --git a/docs/images/favicon.ico b/docs/images/favicon.ico
deleted file mode 100644
index 152f0633..00000000
--- a/docs/images/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/docs/images/favicon.svg b/docs/images/favicon.svg
index 6f640f92..a1d31916 100644
--- a/docs/images/favicon.svg
+++ b/docs/images/favicon.svg
@@ -1,43 +1,23 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- id="svg1021"
- version="1.1"
- viewBox="0 0 8.4666666 8.4666664"
- height="32"
- width="32">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="matrix(0.98177945,0,0,0.98177945,0.07713364,0.07788224)"
- id="g830">
- <g
- transform="translate(-1.4257123e-8,2.7257414e-4)"
- id="g843">
- <path
- id="rect934"
- d="m 4.5488056,1.6366284 c -0.2392871,-0.00227 -0.6920094,0.00685 -0.6920094,0.00685 -0.021958,0 -0.039615,0.018423 -0.039615,0.041303 V 1.9657515 H 3.4048552 V 1.6847784 c 0,-0.022882 -0.017656,-0.041303 -0.039614,-0.041303 H 2.8735865 c -0.021957,0 -0.039713,0.018423 -0.039713,0.041303 v 1.0410915 c 0,0.022882 0.017743,0.041303 0.039713,0.041303 h 0.4916544 c 0.021958,0 0.039614,-0.018419 0.039614,-0.041303 V 2.4449961 h 0.4123264 v 0.2808738 c 0,0.022882 0.017658,0.041303 0.039615,0.041303 H 4.1056018 V 4.0919192 C 3.9629425,4.1485407 3.8624554,4.2872367 3.8624554,4.4505325 v 2.0077214 c 0,0.2138923 0.1721231,0.3860163 0.3860163,0.3860163 0.2138926,0 0.3861152,-0.172124 0.3861152,-0.3860163 V 4.4505325 c 0,-0.1632958 -0.1004863,-0.3019918 -0.2431464,-0.3586133 V 2.7671726 H 4.6608974 C 4.7038181,2.7293845 4.7451842,2.5778556 4.9744364,2.4619735 5.5900645,2.2504932 5.8573916,2.7743693 5.8527022,2.7020414 5.8476858,2.6248234 5.8135784,2.140583 5.2760608,1.8565394 5.041879,1.7512335 4.7665404,1.6477795 4.6922707,1.6434758 c -0.00766,-0.00404 -0.063703,-0.00609 -0.1434651,-0.00685 z"
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.27757847;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- <path
- id="path944"
- d="M 5.0870266,0.02653999 C 4.8758199,0.03005205 4.7086055,0.11753906 4.6071872,0.19293959 4.4719626,0.29347485 4.4285916,0.35098765 4.2240501,0.35149698 4.0195087,0.35201572 3.9760395,0.29458822 3.8403173,0.19472732 3.7045953,0.09486662 3.4517075,-0.02558771 3.1347059,0.06019763 2.8177043,0.14598316 2.6601506,0.37751587 2.5933102,0.53219385 2.5264701,0.68687087 2.5177153,0.75840947 2.3408309,0.86112094 2.1639468,0.96383337 2.0974656,0.93603597 1.9299939,0.91741509 1.7625249,0.89879421 1.4831239,0.92071966 1.2514869,1.1535129 1.019847,1.3863056 0.99901588,1.665936 1.0184673,1.8333106 1.0379159,2.0006862 1.0660866,2.0669533 0.96425832,2.2443463 0.86242717,2.4217392 0.79126264,2.4307061 0.63691896,2.498315 0.48257528,2.5659238 0.25151385,2.7244718 0.16730548,3.0418942 0.08309731,3.3593177 0.20517818,3.6115864 0.30570749,3.7468112 0.40623986,3.882036 0.46375536,3.9254064 0.46426411,4.1299487 0.46476364,4.3344901 0.40735381,4.3778588 0.30749464,4.5135806 0.2076326,4.6493044 0.08727894,4.9021925 0.17306408,5.2191932 c 0.0857851,0.3170006 0.31721758,0.4746537 0.4718969,0.5414944 0.15467644,0.066841 0.22621523,0.075596 0.32892786,0.2524808 0.10270996,0.176883 0.0749156,0.2432665 0.0562942,0.4107372 -0.018621,0.1674688 0.00329,0.4469683 0.2360972,0.6786053 0.2327935,0.2316371 0.5124223,0.2518776 0.6797977,0.2324248 0.1673754,-0.019455 0.2336429,-0.047027 0.4110359,0.054804 0.1773929,0.1018308 0.1863587,0.1729963 0.2539683,0.327339 0.06761,0.1543427 0.2261578,0.3853054 0.54358,0.4695143 C 3.4720844,8.2708 3.8613006,8.1099231 3.8595787,8.048191 3.8578304,7.9864608 3.5971671,8.0926721 3.3358556,7.9282562 3.0745441,7.7638403 3.0933309,7.66234 3.03572,7.5308219 2.9781088,7.3993057 2.8750532,7.1523792 2.587552,6.9873428 2.3000535,6.8223063 2.0349869,6.8580783 1.8923654,6.8746549 1.7497437,6.8912354 1.7402119,6.9201408 1.593024,6.773683 1.4458361,6.6272232 1.4751935,6.6178378 1.4910593,6.475136 1.506927,6.3324343 1.5410636,6.0674191 1.3745991,5.7807437 1.2081346,5.4940663 0.96093453,5.3921126 0.82913284,5.3351567 0.69733114,5.2782009 0.67477725,5.2989947 0.62053712,5.0985628 0.56629699,4.898131 0.59570787,4.9038525 0.68080252,4.7882002 0.76589409,4.6725497 0.92844829,4.4603559 0.92762253,4.1288555 0.9268156,3.7973556 0.76319183,3.585332 0.677526,3.4701069 0.59186035,3.3548807 0.56212996,3.3612378 0.6153743,3.1605391 0.66861576,2.9598403 0.69155948,2.980563 0.82307644,2.9229519 0.9545936,2.865342 1.2015197,2.7622845 1.3665571,2.4747841 1.5315916,2.1872846 1.495821,1.9222192 1.4792445,1.7795973 1.4626756,1.6369766 1.4338566,1.6274456 1.5803154,1.4802567 1.7267744,1.3330676 1.7360605,1.3618297 1.8787632,1.377696 2.0214631,1.3935618 2.28648,1.4282961 2.5731556,1.2618316 2.8598312,1.0953669 2.9612899,0.84816721 3.0182459,0.71636532 3.0752017,0.58456324 3.0550029,0.56200954 3.2554352,0.50776961 c 0.2004324,-0.0542399 0.1946112,-0.024825 0.3102624,0.0602654 0.1156515,0.0850914 0.327846,0.2476448 0.6593453,0.24681943 C 4.5565433,0.81402829 4.7679708,0.65052365 4.8831964,0.56485801 4.998422,0.4791914 4.9921642,0.44936437 5.1928634,0.5026066 5.3935621,0.55584863 5.3733362,0.57879196 5.4309463,0.71030874 5.4885567,0.8418257 5.5917135,1.0887537 5.8792135,1.25379 6.166714,1.4188262 6.4316795,1.3831524 6.5743008,1.3665765 6.7169227,1.3499999 6.7264529,1.3210889 6.873642,1.4675477 c 0.1471893,0.1464588 0.1179306,0.155746 0.1020644,0.2984484 -0.015866,0.1427014 -0.050103,0.4077161 0.1163609,0.6943914 0.166464,0.2866762 0.4136644,0.388136 0.5454663,0.4450911 0.1318018,0.056955 0.1544547,0.036757 0.208695,0.2371894 0.05424,0.2004312 0.024726,0.1946122 -0.060365,0.3102619 C 7.7007719,3.5685807 7.5382193,3.7807747 7.5390439,4.112275 7.53987,4.4437754 7.7034742,4.6552027 7.7891402,4.7704285 7.874806,4.8856543 7.9045341,4.8793967 7.8512923,5.0800957 7.7980493,5.2807946 7.7751067,5.2606675 7.64359,5.3182785 7.5120726,5.3758875 7.2651454,5.478946 7.1001091,5.7664456 6.9350734,6.0539472 6.9708458,6.3189124 6.9874217,6.4615335 7.0039983,6.6041546 7.0329087,6.613686 6.8864501,6.7608739 6.7399909,6.9080638 6.7306052,6.8788049 6.587903,6.8629391 6.445201,6.8470695 6.1801864,6.8129344 5.8935104,6.9793983 5.6068354,7.1458642 5.5047798,7.3930635 5.4478249,7.524864 5.4004987,7.6343856 5.4051276,7.6685284 5.2936367,7.708043 5.2777171,7.6992052 5.259198,7.69079 5.2372436,7.6825286 5.0523914,7.6129808 5.0281647,7.7070266 4.8764452,7.8200353 4.740011,7.9216605 4.6423578,7.9336011 4.648092,8.0676515 4.635208,8.0663115 4.623051,8.0661115 4.613343,8.0672672 4.582367,8.0708572 4.570122,8.0759872 4.537292,8.0571422 4.504462,8.0382942 4.502843,8.0251412 4.490331,7.9965802 4.477818,7.9680152 4.4485,7.9251902 4.389756,7.9096062 4.331009,7.8940252 4.2589764,7.9238042 4.2592973,7.9352202 c 3.074e-4,0.011412 0.048542,-0.00818 0.096901,0.022241 0.04836,0.030427 0.044838,0.04923 0.055499,0.073569 0.010659,0.024342 0.029796,0.070032 0.083002,0.1005762 0.053206,0.03054 0.1021781,0.023918 0.1285724,0.020848 0.020154,-0.00234 0.026174,-0.0058 0.039912,0.00467 0.040491,0.1388419 0.138553,0.1235179 0.3084754,0.187448 0.1848522,0.069548 0.2413459,0.1532207 0.3930652,0.04021 0.1111516,-0.082793 0.099986,-0.1425727 0.073172,-0.240169 C 5.6849116,8.0413129 5.8145317,7.8453114 5.8734551,7.7089556 5.940296,7.5542786 5.9490507,7.4828383 6.1259346,7.3801257 6.3028185,7.277415 6.369203,7.3051138 6.5366727,7.3237347 6.7041423,7.3423518 6.9836412,7.3205262 7.2152788,7.0877345 7.4469164,6.8549428 7.4676519,6.5752146 7.4481991,6.4078381 7.4287446,6.2404634 7.4006768,6.1742951 7.5025074,5.9969011 7.6043374,5.819509 7.6755037,5.8104426 7.8298466,5.7428333 7.9841901,5.675226 8.2151531,5.5167756 8.2993607,5.1993542 8.3835683,4.8819309 8.2614924,4.6296614 8.1609587,4.4944372 8.0604238,4.3592129 8.00301,4.3158423 8.0025016,4.111299 8.0020021,3.9067581 8.0594102,3.8632905 8.1592709,3.7275669 8.2591316,3.5918443 8.3794874,3.338956 8.2937013,3.0219559 8.2079159,2.7049558 7.976383,2.5474003 7.821705,2.48056 7.6670276,2.4137197 7.5955884,2.4049654 7.4928763,2.2280805 7.3901654,2.0511968 7.4178631,1.9847138 7.4364832,1.8172442 7.4551041,1.6497735 7.4332747,1.3703747 7.2004851,1.1387367 6.9676924,0.90709964 6.6879635,0.88626451 6.5205883,0.90571768 6.3532136,0.925172 6.2870448,0.95333828 6.1096519,0.85150886 5.932259,0.74967848 5.923193,0.67851165 5.8555841,0.52416912 5.7879751,0.36983505 5.6295262,0.13877343 5.3121033,0.05456506 5.2327476,0.03351302 5.1574288,0.02539703 5.0870266,0.02656727 Z"
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:#336790;stroke-width:0.19807783;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:normal" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="32" height="32" version="1.1" viewBox="0 0 8.4667 8.4667" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <style>#snake { fill: #336790 }
+ #background { fill: none }
+ #hammer { fill:#e5b62f }
+
+ @media (prefers-color-scheme: dark) {
+ #snake { fill: #FFFFFF }
+ #background { fill: #336790 }
+ }</style>
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect id="background" y="2.7838e-7" width="8.4667" height="8.4667" ry=".50736" style="paint-order:normal"/>
+ <path id="snake" transform="scale(.26458)" d="m19.164 0.029297c-0.88194 0.014665-1.5718 0.37353-1.9941 0.6875-0.26272 0.19532-0.41783 0.32613-0.56055 0.39844-0.14272 0.072303-0.29746 0.11633-0.64258 0.11719-0.34503 8.75e-4 -0.49969-0.039683-0.64258-0.11133-0.14289-0.071645-0.30455-0.20433-0.56836-0.39844-0.56551-0.41609-1.6221-0.9144-2.9297-0.56055-1.3076 0.35385-1.9735 1.3204-2.252 1.9648-0.12988 0.30055-0.19383 0.49294-0.28125 0.62695-0.087417 0.13401-0.20354 0.24664-0.50195 0.41992-0.29844 0.1733-0.45159 0.21312-0.61133 0.22266-0.15973 0.0095357-0.36213-0.024369-0.6875-0.060547-0.69753-0.07756-1.8647 0.022023-2.8203 0.98242-0.95556 0.96031-1.0479 2.1227-0.9668 2.8203 0.037788 0.32522 0.075118 0.52585 0.066406 0.68555-0.0087118 0.15969-0.048898 0.31398-0.2207 0.61328-0.17186 0.29938-0.28672 0.41407-0.41992 0.50195-0.1332 0.087889-0.32119 0.15574-0.62109 0.28711-0.64266 0.28151-1.6096 0.95019-1.957 2.2598-0.34742 1.3096 0.15757 2.3666 0.57617 2.9297 0.19532 0.26272 0.32613 0.41978 0.39844 0.5625 0.072304 0.14272 0.11828 0.29551 0.11914 0.64062 8.427e-4 0.34504-0.039708 0.49979-0.11133 0.64258-0.071621 0.14279-0.20432 0.29866-0.39844 0.5625-0.41617 0.56562-0.91438 1.63-0.56055 2.9375 0.35383 1.3075 1.3183 1.9656 1.9629 2.2441 0.30055 0.12988 0.49294 0.19969 0.62695 0.28711 0.13401 0.08742 0.24859 0.20354 0.42188 0.50195 0.17331 0.29846 0.21117 0.45366 0.2207 0.61328 0.00952 0.15962-0.022405 0.36018-0.058594 0.68555-0.07756 0.69753 0.021947 1.8647 0.98242 2.8203 0.96054 0.95576 2.1216 1.0478 2.8184 0.9668 0.32479-0.03776 0.52777-0.07552 0.6875-0.06641 0.15973 0.0091 0.31957 0.05069 0.61914 0.22266 0.29938 0.17186 0.40821 0.27891 0.49609 0.41211 0.087889 0.1332 0.15574 0.329 0.28711 0.62891 0.28154 0.6427 0.95026 1.5999 2.2598 1.9473 0.68097 0.18065 1.3929 0.10085 1.9473-0.05078 0.27718-0.07582 0.51383-0.17138 0.70117-0.27344 0.09367-0.05103 0.17632-0.1008 0.25195-0.16992 0.07563-0.06912 0.18398-0.15909 0.17774-0.38281-0.0045-0.15928-0.15659-0.33832-0.26562-0.38477-0.10904-0.04644-0.1808-0.04758-0.25195-0.05078-0.14232-0.0064-0.28263 0.0029-0.44922 0-0.33318-0.0058-0.73652-0.05383-1.1445-0.31055-0.90522-0.56956-0.72117-0.72807-0.97461-1.3066-0.21057-0.48069-0.64521-1.521-1.8164-2.1934-1.1713-0.67236-2.2838-0.52538-2.8047-0.46484-0.28801 0.03348-0.39727 0.05848-0.42188 0.05273-0.024608-0.0057-0.12892-0.05795-0.39062-0.31836-0.26164-0.26035-0.31078-0.35895-0.31641-0.38281-0.00563-0.02385 0.010884-0.14107 0.042969-0.42969 0.058004-0.52165 0.20538-1.6292-0.47266-2.7969-0.67803-1.1677-1.7171-1.6003-2.1992-1.8086-0.26649-0.11516-0.37278-0.15326-0.39062-0.16992s-0.081293-0.10847-0.17773-0.46484c-0.096489-0.35655-0.08929-0.46948-0.082031-0.49414 0.00726-0.02464 0.080382-0.11448 0.25195-0.34766 0.31051-0.42202 0.99165-1.3134 0.98828-2.6641-0.00329-1.3506-0.69092-2.2372-1.0039-2.6582-0.17301-0.23272-0.24275-0.32375-0.25-0.34766-0.00725-0.02393-0.014608-0.13724 0.080078-0.49414 0.094621-0.35668 0.15194-0.44794 0.16992-0.46484 0.017979-0.01689 0.12654-0.05338 0.39258-0.16992 0.48071-0.21057 1.5191-0.64521 2.1914-1.8164 0.67236-1.1713 0.52538-2.2838 0.46484-2.8047-0.033451-0.28794-0.05655-0.39526-0.050781-0.41992 0.0057827-0.02468 0.055978-0.13085 0.31641-0.39258 0.26055-0.26185 0.36074-0.31065 0.38477-0.31641 0.024038-0.00574 0.13179 0.012875 0.41992 0.044922 0.52063 0.057887 1.6386 0.19776 2.8066-0.48047 1.1678-0.67812 1.5928-1.712 1.8008-2.1934 0.11497-0.26606 0.16049-0.37226 0.17773-0.39062 0.01723-0.018368 0.10838-0.081269 0.46484-0.17773 0.35656-0.096492 0.46964-0.087296 0.49414-0.080078 0.02449 0.00722 0.10662 0.078405 0.33984 0.25 0.42203 0.31051 1.3135 0.98578 2.6641 0.98242 1.3505-0.00337 2.2454-0.68338 2.666-0.99609 0.23273-0.17302 0.31379-0.24464 0.33789-0.25195 0.02411-0.00731 0.13926-0.012632 0.49609 0.082031 0.35676 0.094642 0.44745 0.15151 0.46484 0.16992 0.01739 0.01841 0.0594 0.12495 0.17578 0.39062 0.21063 0.48084 0.64524 1.5133 1.8164 2.1855 1.1712 0.67231 2.2837 0.5254 2.8047 0.46484 0.28802-0.033487 0.39727-0.050663 0.42188-0.044922 0.0246 0.00574 0.12892 0.057957 0.39062 0.31836 0.2617 0.2604 0.31274 0.35905 0.31836 0.38281 0.0056 0.023773-0.02064 0.13333-0.05273 0.42188-0.05798 0.52147-0.1976 1.637 0.48047 2.8047 0.67812 1.1678 1.7179 1.5928 2.1992 1.8008 0.26609 0.11498 0.37412 0.15261 0.39258 0.16992 0.01844 0.01731 0.07933 0.11624 0.17578 0.47266 0.09648 0.35651 0.08927 0.46962 0.08203 0.49414-0.0073 0.02453-0.08032 0.10657-0.25195 0.33984-0.31051 0.42202-0.99164 1.3135-0.98828 2.6641 0.0034 1.3506 0.69127 2.2455 1.0039 2.666 0.17298 0.23267 0.24271 0.31577 0.25 0.33984 0.0073 0.02407 0.01461 0.13725-0.08008 0.49414-0.09465 0.35678-0.15923 0.44736-0.17773 0.46484-0.01848 0.0175-0.1172 0.06139-0.38281 0.17773-0.48071 0.21057-1.521 0.64522-2.1934 1.8164-0.67234 1.1713-0.52538 2.2838-0.46484 2.8047 0.03349 0.28802 0.05652 0.39531 0.05078 0.41992-0.0057 0.02461-0.056 0.12307-0.31641 0.38477-0.26038 0.26167-0.36085 0.31075-0.38477 0.31641-0.02392 0.0057-0.13138-0.01088-0.41992-0.04297-0.52166-0.05801-1.637-0.19952-2.8047 0.47852-1.1676 0.67802-1.6002 1.719-1.8086 2.2012-0.09505 0.21997-0.14146 0.345-0.16211 0.375-0.0078 0.01134-0.06163 0.02709-0.07422 0.03711-0.02506-0.01043-0.04775-0.01936-0.07422-0.0293-0.39901-0.15012-0.77353-0.12686-1.0332 0.0293-0.25968 0.15617-0.40837 0.34516-0.6582 0.53125-0.22497 0.16758-0.42334 0.25977-0.62695 0.42188-0.1078 0.08583-0.13134 0.27743-0.19922 0.42774-0.01406 0.0015-0.03043 0.0072-0.0293 0.0078 0.0011-5.19e-4 0.0011-0.0012-0.02148-0.05273-0.07693-0.1756-0.26331-0.43841-0.61328-0.53125-0.20104-0.05332-0.3768-0.0307-0.51758 0.0078-0.07039 0.01924-0.12868 0.04787-0.19141 0.08203-0.03137 0.01708-0.06926 0.03382-0.11133 0.07227-0.02105 0.0192-0.06446 0.07891-0.06641 0.08203-9.72e-4 0.0016-0.04249 0.08644-0.04297 0.08789-4.78e-4 0.0015-0.01566 0.12368-0.01563 0.125 3.5e-5 0.0013 0.01525 0.11022 0.01563 0.11133 3.77e-4 0.0011 0.0657 0.11042 0.06641 0.11133 0.0014 0.0018 0.14381 0.11605 0.14648 0.11719 0.0027 0.0011 0.0723 0.02559 0.0957 0.0293 0.0234 0.0037 0.04672 0.0072 0.06055 0.0078 0.05531 0.0025 0.06953-3.35e-4 0.08789 0 0.03674 6.71e-4 0.04558-3.61e-4 0.05859 0.0078 0.11488 0.07228-0.01299-0.06995 0.06641 0.11133 0.03659 0.08358 0.15732 0.37174 0.45898 0.54492 0.2858 0.16405 0.55768 0.12343 0.66406 0.11133 0.12223 0.17551 0.22708 0.38193 0.39062 0.45117 0.24961 0.10568 0.48318 0.143 0.76758 0.25 0.30905 0.11628 0.51148 0.25905 0.80469 0.33984 0.29321 0.0808 0.67005 0.01236 1.0039-0.23633 0.24494-0.18239 0.41372-0.40308 0.46484-0.65039 0.03446-0.16668-0.0068-0.29245-0.03711-0.42774 0.78889-0.45096 1.366-1.0846 1.5801-1.5801 0.12987-0.30053 0.19385-0.49304 0.28125-0.62695 0.0874-0.13391 0.2035-0.24661 0.50195-0.41992 0.29851-0.17333 0.45365-0.21892 0.61328-0.22852s0.36024 0.03024 0.68555 0.06641c0.6974 0.07753 1.8646-0.02197 2.8203-0.98242 0.95562-0.96038 1.0479-2.1307 0.9668-2.8281-0.03779-0.32515-0.07518-0.52597-0.06641-0.68555 0.0088-0.15958 0.04885-0.31391 0.2207-0.61328 0.17187-0.29941 0.28865-0.41398 0.42188-0.50195 0.13323-0.08797 0.3193-0.15576 0.61914-0.28711 0.6426-0.28148 1.6018-0.95024 1.9492-2.2598 0.34741-1.3096-0.14973-2.3588-0.56836-2.9219-0.19529-0.26267-0.32621-0.4198-0.39844-0.5625-0.07223-0.1427-0.11828-0.2974-0.11914-0.64258-8.43e-4 -0.34505 0.03969-0.49773 0.11133-0.64062 0.07164-0.1429 0.20434-0.30455 0.39844-0.56836 0.41616-0.56561 0.91438-1.6241 0.56055-2.9316-0.35385-1.3076-1.3185-1.9734-1.9629-2.2519-0.30053-0.12987-0.49304-0.1919-0.62695-0.2793-0.13391-0.087395-0.24857-0.20349-0.42188-0.50195-0.17332-0.29848-0.2111-0.45354-0.2207-0.61328-0.009603-0.15974 0.022426-0.36025 0.058594-0.68555 0.077543-0.6974-0.02007-1.8647-0.98047-2.8203-0.96035-0.95558-2.1305-1.0479-2.8281-0.9668-0.32522 0.037802-0.52011 0.075116-0.67969 0.066406s-0.31982-0.050834-0.61914-0.22266c-0.29941-0.17187-0.40617-0.28669-0.49414-0.41992-0.087972-0.13323-0.15772-0.32125-0.28906-0.62109-0.28146-0.6425-0.95603-1.6077-2.2656-1.9551-0.28592-0.075851-0.5516-0.09112-0.8125-0.095703-0.02855-0.0087601-0.05807-0.014007-0.08789-0.015625-0.0092 7.604e-5 -0.02011-1.5289e-4 -0.0293 0z" color="#000000" color-rendering="auto" dominant-baseline="auto" image-rendering="auto" shape-rendering="auto" solid-color="#000000" style="font-feature-settings:normal;font-variant-alternates:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal;font-variant-position:normal;isolation:auto;mix-blend-mode:normal;paint-order:normal;shape-padding:0;text-decoration-color:#000000;text-decoration-line:none;text-decoration-style:solid;text-indent:0;text-orientation:mixed;text-transform:none;white-space:normal"/>
+ <path id="hammer" transform="scale(.26458)" d="m17.178 5.8535c-0.90458-0.00858-2.5666 0.023249-2.5762 0.023437-0.36953 0-0.65625 0.32007-0.65625 0.66992v0.52539h-0.50195v-0.52539c0-0.34982-0.29651-0.66992-0.66602-0.66992h-1.8223c-0.36954 0-0.66406 0.31858-0.66406 0.66992v3.8613c0 0.35114 0.29466 0.67188 0.66406 0.67188h1.8223c0.36947 0 0.66602-0.32207 0.66602-0.67188v-0.52344h0.50195v0.52344c0 0.34986 0.28674 0.67188 0.65625 0.67188h0.40625v4.4004c-0.42951 0.35793-0.90039 0.7288-0.90039 1.3281v7.4492c0 1.07 0.87729 1.9473 1.9473 1.9473 1.07 0 1.9492-0.87714 1.9492-1.9473v-7.4492c0-0.60046-0.46946-0.97021-0.90039-1.3281v-4.4004h0.67969l0.14062-0.13281c0.2743-0.2415 0.26214-0.3581 0.39062-0.52344 0.12134-0.15616 0.30876-0.33151 0.62695-0.50195 0.97792-0.32632 1.5844-0.096178 2.0156 0.19141 0.21798 0.14536 0.38435 0.30454 0.50195 0.42774 0.0588 0.06159 0.0867 0.10559 0.16992 0.17773 0.02079 0.01803 0.04559 0.03682 0.0957 0.06641 0.02506 0.01482 0.10911 0.05004 0.11133 0.05078 0.0022 7.36e-4 0.21117 0.02363 0.21289 0.02344 8.58e-4 -8.7e-5 0.16333-0.03677 0.16406-0.03711 7.33e-4 -3.22e-4 0.13807-0.09514 0.13867-0.0957 0.0012-0.0011 0.13991-0.22055 0.14063-0.22266 7.18e-4 -0.0021 0.02688-0.09873 0.0293-0.13281 0.0024-0.03409 0.0014-0.05869 0-0.08008-0.0126-0.20888-0.08797-0.32205-0.11524-0.42188-0.32614-0.76148-0.35937-2.1118-2.2988-3.1367l-0.01367-0.00781-0.01563-0.00781c-0.44578-0.2005-0.92355-0.40081-1.3359-0.55274-0.2062-0.075966-0.39695-0.13657-0.56055-0.18555-0.1205-0.036072-0.2224-0.055177-0.33203-0.072266-0.0052-8.054e-4 -0.01037-0.00706-0.01563-0.00781-0.0243-0.00677-0.04332-0.01279-0.05859-0.015625-0.01822-0.00334-0.03075-0.00412-0.04492-0.00586-0.0096-0.00119-0.02019-0.00691-0.0293-0.00781-0.04519-0.00461-0.08879-0.00551-0.14062-0.00781-0.10369-0.00461-0.23158-0.00638-0.38281-0.00781z" style="paint-order:markers fill stroke"/>
</svg>
diff --git a/docs/images/favicon.xcf b/docs/images/favicon.xcf
deleted file mode 100644
index 2cf5ff27..00000000
--- a/docs/images/favicon.xcf
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-demo-editable-inkscape.png b/docs/images/logo-demo-editable-inkscape.png
deleted file mode 100644
index 70ca467b..00000000
--- a/docs/images/logo-demo-editable-inkscape.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-demo.svg b/docs/images/logo-demo.svg
new file mode 100644
index 00000000..6b78ebc3
--- /dev/null
+++ b/docs/images/logo-demo.svg
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="242.7mm" height="125.92mm" version="1.1" viewBox="0 0 242.7 125.92" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(113.76 -68.446)">
+ <path id="rect1633" d="m-111.69 133.32h61.237c0.87182 0 1.5737 0.70186 1.5737 1.5737v57.404c0 0.87182-0.70186 1.5737-1.5737 1.5737h-61.237c-0.87182 0-1.5737-0.70186-1.5737-1.5737v-57.404c0-0.87182 0.70186-1.5737 1.5737-1.5737z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <path id="rect1616" d="m-111.69 68.946h61.237c0.87182 0 1.5737 0.70186 1.5737 1.5737v57.404c0 0.87182-0.70186 1.5737-1.5737 1.5737h-61.237c-0.87182 0-1.5737-0.70186-1.5737-1.5737v-57.404c0-0.87182 0.70186-1.5737 1.5737-1.5737z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <path id="rect934" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m129.77 43.688c-4.7073-0.044694-13.613 0.13281-13.613 0.13281-0.43194 0-0.77929 0.36438-0.77929 0.81445v5.5254h-8.1113v-5.5254c0-0.45012-0.34735-0.81445-0.7793-0.81445h-9.6719c-0.43195 0-0.7793 0.36438-0.7793 0.81445v20.48c0 0.45012 0.34707 0.8125 0.7793 0.8125h9.6719c0.43195 0 0.7793-0.36232 0.7793-0.8125v-5.5254h8.1113v5.5254c0 0.45012 0.34739 0.8125 0.77929 0.8125h4.8945v26.061c-2.8064 1.1139-4.7832 3.8423-4.7832 7.0547v39.494c0 4.2077 3.388 7.5957 7.5957 7.5957 4.2077 0 7.5957-3.388 7.5957-7.5957v-39.494c0-3.2119-1.9775-5.9405-4.7832-7.0547v-26.061h5.3008c0.84434-0.74337 1.6581-3.7262 6.168-6.0059 12.111-4.1603 17.37 6.1474 17.277 4.7246-0.09865-1.519-0.7716-11.047-11.346-16.635-4.6068-2.0716-10.023-4.1068-11.484-4.1914-0.15078-0.079455-1.2532-0.11791-2.8223-0.13281z" fill="#e5b62f" stroke="#e5b62f" stroke-width="1.8045" style="paint-order:markers fill stroke"/>
+ <path id="path944" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m140.35 12.014c-4.1549 0.06908-7.4443 1.7901-9.4395 3.2734-2.6602 1.9777-3.5133 3.1091-7.5371 3.1191-4.0238 0.010205-4.8769-1.1215-7.5469-3.0859-2.6699-1.9645-7.6448-4.3321-13.881-2.6445-6.2361 1.6876-9.3375 6.2423-10.652 9.2852-1.3149 3.0428-1.4871 4.4482-4.9668 6.4688-3.4797 2.0206-4.7856 1.4757-8.0801 1.1094-3.2945-0.36631-8.7928 0.065007-13.35 4.6445-4.5568 4.5795-4.9647 10.08-4.582 13.373 0.38259 3.2926 0.93482 4.5962-1.0684 8.0859-2.0032 3.4897-3.4032 3.6661-6.4395 4.9961-3.0363 1.33-7.5798 4.449-9.2363 10.693-1.6566 6.2444 0.74506 11.207 2.7227 13.867 1.9777 2.6602 3.1072 3.5133 3.1172 7.5371 0.011339 4.0238-1.1195 4.8769-3.084 7.5469-1.9645 2.67-4.3321 7.6448-2.6445 13.881 1.6876 6.236 6.2423 9.3374 9.2852 10.652 3.0428 1.3149 4.4482 1.4871 6.4688 4.9668 2.0205 3.4797 1.4757 4.7856 1.1094 8.0801-0.36631 3.2945 0.062774 8.7928 4.6426 13.35 4.5795 4.5568 10.08 4.9549 13.373 4.5723 3.2926-0.38272 4.5962-0.9251 8.0859 1.0781 3.4897 2.0032 3.668 3.4032 4.998 6.4394 1.33 3.0362 4.447 7.5798 10.691 9.2363 6.2444 1.6565 13.901-1.5082 13.867-2.7226-0.03439-1.2144-5.1602 0.87503-10.301-2.3594-5.1406-3.2344-4.7729-5.2312-5.9062-7.8184-1.1333-2.5872-3.1606-7.4448-8.8164-10.691-5.6557-3.2466-10.868-2.5429-13.674-2.2168-2.8057 0.32617-2.9932 0.89285-5.8887-1.9883-2.8955-2.8812-2.3199-3.0638-2.0078-5.8711 0.31219-2.8073 0.98369-8.0206-2.291-13.66-3.2747-5.6395-8.1377-7.6472-10.73-8.7676-2.5928-1.1204-3.0365-0.71142-4.1035-4.6543-1.067-3.943-0.4865-3.8284 1.1875-6.1035 1.6739-2.2751 4.8698-6.4494 4.8535-12.971-0.015874-6.5213-3.2327-10.692-4.918-12.959-1.6852-2.2667-2.2701-2.1417-1.2227-6.0898 1.0474-3.9482 1.4987-3.5405 4.0859-4.6738 2.5872-1.1333 7.4448-3.1626 10.691-8.8184 3.2466-5.6557 2.5429-10.868 2.2168-13.674-0.32591-2.8057-0.89483-2.9932 1.9863-5.8887 2.8812-2.8955 3.0638-2.3297 5.8711-2.0176 2.8072 0.31211 8.0226 0.99541 13.662-2.2793 5.6395-3.2747 7.6354-8.1376 8.7559-10.73 1.1204-2.5928 0.72115-3.0365 4.6641-4.1035 3.9429-1.067 3.8284-0.48836 6.1035 1.1855 2.2751 1.6739 6.4514 4.8717 12.973 4.8555 6.5213-0.016252 10.681-3.2347 12.947-4.9199 2.2667-1.6852 2.1417-2.2701 6.0898-1.2227 3.9482 1.0474 3.5522 1.4987 4.6855 4.0859 1.1333 2.5872 3.1607 7.4448 8.8164 10.691s10.87 2.5429 13.676 2.2168c2.8057-0.3261 2.9932-0.89287 5.8887 1.9883s2.318 3.0638 2.0059 5.8711c-0.31211 2.8072-0.98369 8.0207 2.291 13.66 3.2747 5.6395 8.1376 7.6355 10.73 8.7559 2.5928 1.1204 3.0365 0.72311 4.1035 4.666 1.067 3.9429 0.48642 3.8284-1.1875 6.1035-1.6739 2.2751-4.8697 6.4494-4.8535 12.971 0.01625 6.5213 3.2327 10.681 4.918 12.947 1.6852 2.2667 2.27 2.1436 1.2226 6.0918-1.0474 3.9482-1.4968 3.5503-4.084 4.6836-2.5872 1.1333-7.4448 3.1626-10.691 8.8184-3.2466 5.6557-2.5448 10.868-2.2188 13.674 0.3261 2.8056 0.89483 2.9932-1.9863 5.8887-2.8812 2.8955-3.0638 2.32-5.8711 2.0078-2.8072-0.31219-8.0206-0.9856-13.66 2.2891-5.6395 3.2747-7.6472 8.1376-8.7676 10.73-0.93129 2.1552-0.84174 2.8278-3.0371 3.6055-0.31286-0.1736-0.67425-0.33971-1.1055-0.50195-3.6364-1.3682-4.115 0.48194-7.0996 2.7051-2.684 1.9992-4.6031 2.234-4.4902 4.8711-0.25407-0.02563-0.4922-0.03006-0.68359-0.00781-0.60936 0.07067-0.85026 0.17155-1.4961-0.19922s-0.67964-0.63142-0.92579-1.1934c-0.24616-0.56187-0.82291-1.4024-1.9785-1.709-1.1557-0.30652-2.5727 0.2791-2.5664 0.50391 0.00643 0.2245 0.95492-0.16129 1.9062 0.4375 0.95134 0.5986 0.88402 0.96652 1.0938 1.4453 0.20965 0.47887 0.5842 1.3796 1.6309 1.9805 1.0467 0.60083 2.012 0.47055 2.5312 0.41016 0.39726-0.04627 0.51372-0.11363 0.78516 0.09375 0.79695 2.7289 2.7246 2.4282 6.0664 3.6855 3.6364 1.3682 4.7478 3.0142 7.7324 0.79102 2.1873-1.6292 1.9673-2.8055 1.4394-4.7266 4.8594-2.0322 7.4092-5.8859 8.5684-8.5684 1.3149-3.0428 1.4871-4.4482 4.9668-6.4688 3.4797-2.0206 4.7875-1.4757 8.082-1.1094 3.2945 0.36624 8.7908-0.06484 13.348-4.6445 4.5568-4.5795 4.9667-10.08 4.584-13.373-0.38271-3.2926-0.93681-4.5962 1.0664-8.0859 2.0032-3.4897 3.4032-3.666 6.4395-4.9961 3.0362-1.33 7.5817-4.449 9.2383-10.693 1.6565-6.2444-0.74494-11.207-2.7227-13.867-1.9777-2.6601-3.1091-3.5133-3.1191-7.5371-0.00983-4.0238 1.1195-4.8769 3.084-7.5469 1.9645-2.6699 4.3341-7.6448 2.6465-13.881-1.6876-6.2361-6.2423-9.3375-9.2852-10.652s-4.4502-1.4852-6.4707-4.9648-1.4737-4.7876-1.1074-8.082c0.36631-3.2945-0.065-8.7928-4.6445-13.35-4.5795-4.5568-10.08-4.9647-13.373-4.582-3.2926 0.38271-4.5962 0.93681-8.0859-1.0664-3.4897-2.0032-3.6661-3.4032-4.9961-6.4395-1.33-3.0363-4.449-7.5817-10.693-9.2383-1.5611-0.41413-3.0428-0.57381-4.4277-0.55078z" fill="#336790" style="paint-order:markers fill stroke"/>
+ <g id="text1023" fill="#336790" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1513" d="m-108.11 126.82q-0.95915 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14622-0.28073-0.41525-0.386-0.1696-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.164 0.14621v1.4095q-1.4212-0.15206-2.164-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25148 0.25733-0.0877 0.15791-0.12867 0.32167-0.0351 0.16376-0.0351 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61994 0.30413 0.38015 0.4211 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70183 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1515" d="m-104.26 118.49h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1517" d="m-93.406 118.49v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1519" d="m-92.663 118.49h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29243-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52051-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1521" d="m-84.809 119.89v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446 0-0.44448-0.23979-0.75446-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52637 0.66673 0.52637 1.6376t-0.52637 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1661" fill="#e5b62f" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1524" d="m-75.457 118.49v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1526" d="m-73.223 121.1q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60824-1.6668 0.60824-1.0176 0-1.6668-0.60824-0.64918-0.6141-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1528" d="m-67.24 121.1q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60824-1.6668 0.60824-1.0176 0-1.6668-0.60824-0.64918-0.6141-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1530" d="m-62.374 126.73v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1532" d="m-54.262 126.82q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.08773 0.15791-0.12867 0.32167-0.03509 0.16376-0.03509 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <path id="rect905" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m129.77 286.97c-4.7073-0.0447-13.613 0.13476-13.613 0.13476-0.43194 0-0.77929 0.36236-0.77929 0.8125v5.5274h-8.1113v-5.5274c0-0.4501-0.34735-0.8125-0.7793-0.8125h-9.6719c-0.43195 0-0.7793 0.36236-0.7793 0.8125v20.482c0 0.45011 0.34707 0.81055 0.7793 0.81055h9.6719c0.43195 0 0.7793-0.36052 0.7793-0.81055v-5.5273h8.1113v5.5273c0 0.45011 0.34739 0.81055 0.77929 0.81055h4.8945v26.061c-2.8064 1.1138-4.7832 3.8423-4.7832 7.0547v39.496c0 4.2078 3.388 7.5938 7.5957 7.5938 4.2077 0 7.5957-3.386 7.5957-7.5938v-39.496c0-3.2119-1.9775-5.9405-4.7832-7.0547v-26.061h5.3008c0.84434-0.74336 1.6581-3.7242 6.168-6.0039 12.111-4.1602 17.37 6.1455 17.277 4.7227-0.09865-1.519-0.7716-11.045-11.346-16.633-4.6068-2.0716-10.023-4.1068-11.484-4.1914-0.15078-0.07946-1.2532-0.11986-2.8223-0.13476z" stroke="#000" stroke-width="1.8045" style="paint-order:markers fill stroke"/>
+ <path id="path913" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m140.35 255.3c-4.1549 0.06908-7.4443 1.7902-9.4395 3.2734-2.6602 1.9777-3.5133 3.1091-7.5371 3.1191-4.0238 0.01133-4.8769-1.1195-7.5469-3.084-2.6699-1.9645-7.6448-4.3321-13.881-2.6445-6.2361 1.6876-9.3375 6.2404-10.652 9.2832-1.3149 3.0428-1.4871 4.4502-4.9668 6.4707-3.4797 2.0206-4.7856 1.4737-8.0801 1.1074-3.2945-0.36624-8.7928 0.06484-13.35 4.6445-4.5568 4.5795-4.9647 10.08-4.582 13.373 0.38259 3.2926 0.93482 4.5962-1.0684 8.0859-2.0032 3.4897-3.4032 3.6661-6.4395 4.9961-3.0363 1.33-7.5798 4.4489-9.2363 10.693-1.6566 6.2444 0.74506 11.207 2.7227 13.867 1.9777 2.6601 3.1072 3.5133 3.1172 7.5371 0.011339 4.0238-1.1195 4.8789-3.084 7.5488-1.9645 2.67-4.3321 7.6428-2.6445 13.879 1.6876 6.2361 6.2423 9.3374 9.2852 10.652 3.0428 1.3149 4.4482 1.4871 6.4688 4.9668 2.0205 3.4797 1.4757 4.7875 1.1094 8.082-0.36631 3.2945 0.062774 8.7908 4.6426 13.348 4.5795 4.5568 10.08 4.9549 13.373 4.5723 3.2926-0.38271 4.5962-0.92514 8.0859 1.0781 3.4897 2.0032 3.668 3.4032 4.998 6.4394 1.33 3.0363 4.447 7.5797 10.691 9.2363 6.2444 1.6565 13.901-1.5063 13.867-2.7207-0.03439-1.2144-5.1602 0.87304-10.301-2.3613-5.1406-3.2344-4.7729-5.2312-5.9062-7.8184-1.1333-2.5872-3.1606-7.4448-8.8164-10.691-5.6557-3.2466-10.868-2.5429-13.674-2.2168-2.8057 0.32618-2.9932 0.89481-5.8887-1.9863-2.8955-2.8812-2.3199-3.0638-2.0078-5.8711 0.31219-2.8073 0.98369-8.0226-2.291-13.662-3.2747-5.6395-8.1377-7.6452-10.73-8.7656-2.5928-1.1204-3.0365-0.71142-4.1035-4.6543-1.067-3.943-0.4865-3.8303 1.1875-6.1055 1.6739-2.2751 4.8698-6.4494 4.8535-12.971-0.015874-6.5214-3.2327-10.69-4.918-12.957-1.6852-2.2667-2.2701-2.1436-1.2227-6.0918 1.0474-3.9482 1.4987-3.5405 4.0859-4.6738 2.5872-1.1333 7.4448-3.1606 10.691-8.8164 3.2466-5.6557 2.5429-10.87 2.2168-13.676-0.32591-2.8057-0.89483-2.9931 1.9863-5.8887 2.8812-2.8955 3.0638-2.3297 5.8711-2.0176 2.8072 0.31219 8.0226 0.9954 13.662-2.2793 5.6395-3.2747 7.6354-8.1377 8.7559-10.73 1.1204-2.5928 0.72115-3.0365 4.6641-4.1035 3.9429-1.067 3.8284-0.48641 6.1035 1.1875 2.2751 1.6739 6.4514 4.8697 12.973 4.8535 6.5213-0.01512 10.681-3.2328 12.947-4.918 2.2667-1.6852 2.1417-2.2701 6.0898-1.2226 3.9482 1.0474 3.5522 1.4967 4.6855 4.084 1.1333 2.5872 3.1607 7.4448 8.8164 10.691 5.6557 3.2466 10.87 2.5448 13.676 2.2188 2.8057-0.32618 2.9932-0.89485 5.8887 1.9863 2.8955 2.8811 2.318 3.0638 2.0059 5.8711-0.31211 2.8072-0.98369 8.0206 2.291 13.66 3.2747 5.6395 8.1376 7.6355 10.73 8.7559 2.5928 1.1205 3.0365 0.7231 4.1035 4.666 1.067 3.9429 0.48642 3.8284-1.1875 6.1035-1.6739 2.2751-4.8697 6.4494-4.8535 12.971 0.01625 6.5213 3.2327 10.682 4.918 12.949 1.6852 2.2667 2.27 2.1417 1.2226 6.0898-1.0474 3.9482-1.4968 3.5522-4.084 4.6856-2.5872 1.1333-7.4448 3.1607-10.691 8.8164-3.2466 5.6557-2.5448 10.868-2.2188 13.674 0.3261 2.8057 0.89483 2.9932-1.9863 5.8887-2.8812 2.8955-3.0638 2.32-5.8711 2.0078-2.8072-0.31219-8.0206-0.98365-13.66 2.291-5.6395 3.2747-7.6472 8.1376-8.7676 10.73-0.93126 2.1551-0.84198 2.8259-3.0371 3.6035-0.31272-0.17348-0.67455-0.33983-1.1055-0.50196-3.6364-1.3681-4.115 0.48194-7.0996 2.7051-2.6846 1.9997-4.6041 2.2342-4.4902 4.873-0.2541-0.02564-0.49218-0.03202-0.68359-0.00977-0.60936 0.07068-0.85026 0.17156-1.4961-0.19922-0.64583-0.37077-0.67964-0.62946-0.92579-1.1914-0.24616-0.56187-0.82291-1.4044-1.9785-1.7109-1.1557-0.30652-2.5727 0.2791-2.5664 0.50391 0.00643 0.2245 0.95492-0.16129 1.9062 0.4375 0.95134 0.5986 0.88402 0.96847 1.0938 1.4473 0.20965 0.47887 0.5842 1.3777 1.6309 1.9785 1.0467 0.60083 2.012 0.47055 2.5312 0.41015 0.39726-0.04626 0.51372-0.11362 0.78516 0.09375 0.79695 2.7289 2.7246 2.4282 6.0664 3.6856 3.6364 1.3682 4.7478 3.0142 7.7324 0.79102 2.1872-1.6292 1.9673-2.8037 1.4394-4.7246 4.8594-2.0321 7.4092-5.8859 8.5684-8.5684 1.3149-3.0428 1.4871-4.4502 4.9668-6.4707 3.4797-2.0206 4.7875-1.4757 8.082-1.1094 3.2945 0.36624 8.7908-0.06293 13.348-4.6426 4.5568-4.5795 4.9667-10.082 4.584-13.375-0.38271-3.2926-0.93681-4.5942 1.0664-8.084 2.0032-3.4896 3.4032-3.668 6.4395-4.998 3.0362-1.33 7.5817-4.447 9.2383-10.691 1.6565-6.2443-0.74494-11.207-2.7227-13.867-1.9777-2.6602-3.1091-3.5133-3.1191-7.5371-0.00983-4.0237 1.1195-4.8789 3.084-7.5488 1.9645-2.6699 4.3341-7.6448 2.6465-13.881-1.6876-6.236-6.2423-9.3355-9.2852-10.65-3.0428-1.3149-4.4502-1.4871-6.4707-4.9668-2.0206-3.4797-1.4737-4.7876-1.1074-8.082 0.36631-3.2945-0.065-8.7909-4.6445-13.348-4.5795-4.5568-10.08-4.9667-13.373-4.584-3.2926 0.38271-4.5962 0.93678-8.0859-1.0664-3.4897-2.0032-3.6661-3.4032-4.9961-6.4394s-4.449-7.5798-10.693-9.2363c-1.5611-0.41413-3.0428-0.57576-4.4277-0.55273z" style="paint-order:markers fill stroke"/>
+ <g id="text925" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1488" d="m-108.11 191.19q-0.95915 0-2.1639-0.18715v-1.427q1.4095 0.21054 2.1639 0.21054 1.041 0 1.041-1.1112 0-0.30998-0.15206-0.58486-0.14622-0.28072-0.41525-0.386-0.1696-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78955 0 2.164 0.14622v1.4095q-1.4212-0.15207-2.164-0.15207-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25148 0.25733-0.0877 0.15791-0.12867 0.32167-0.0351 0.16376-0.0351 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61995 0.30413 0.38015 0.4211 0.76615t0.11697 0.80125q0 1.1346-0.70183 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1490" d="m-104.26 182.86h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1492" d="m-93.406 182.86v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1494" d="m-92.663 182.86h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67843 0.29242 0.25733 0.70767 0.25733t0.70182-0.25733q0.29243-0.25734 0.38015-0.67843 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52051-1.6376 0.52051t-1.6434-0.52051q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1496" d="m-84.809 184.26v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75445 0-0.44449-0.23979-0.75446-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87142 0.52637 0.66673 0.52637 1.6376 0 0.97085-0.52637 1.6434-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text929" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1499" d="m-75.457 182.86v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1501" d="m-73.223 185.47q-0.08773 0.47958-0.08773 1.5089t0.08773 1.5089q0.10527 0.56731 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36845 0.39185-0.93576 0.08773-0.48542 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.36261-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64919-0.16376-2.0411 0-1.392 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.6141 1.6668-0.6141 1.0176 0 1.6668 0.6141 0.64918 0.60824 0.88312 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.3919-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60824-1.6668 0.60824-1.0176 0-1.6668-0.60824-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1503" d="m-67.24 185.47q-0.08773 0.47958-0.08773 1.5089t0.08773 1.5089q0.10527 0.56731 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36845 0.39185-0.93576 0.08773-0.48542 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.36261-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64919-0.16376-2.0411 0-1.392 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.6141 1.6668-0.6141 1.0176 0 1.6668 0.6141 0.64918 0.60824 0.88312 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.3919-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60824-1.6668 0.60824-1.0176 0-1.6668-0.60824-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1505" d="m-62.374 191.1v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1507" d="m-54.262 191.19q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21054 2.1639 0.21054 1.041 0 1.041-1.1112 0-0.30998-0.15206-0.58486-0.14621-0.28072-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78955 0 2.1639 0.14622v1.4095q-1.4212-0.15207-2.1639-0.15207-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.08773 0.15791-0.12867 0.32167-0.03509 0.16376-0.03509 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61995 0.30412 0.38015 0.42109 0.76615t0.11697 0.80125q0 1.1346-0.70182 1.8247t-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <path id="rect830" d="m-34.068 68.946h61.237c0.87182 0 1.5737 0.70186 1.5737 1.5737v57.404c0 0.87182-0.70186 1.5737-1.5737 1.5737h-61.237c-0.87182 0-1.5737-0.70186-1.5737-1.5737v-57.404c0-0.87182 0.70186-1.5737 1.5737-1.5737z" fill="#336790" style="paint-order:markers fill stroke"/>
+ <path id="rect935" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m423.13 43.688c-4.7073-0.044694-13.613 0.13281-13.613 0.13281-0.43195 0-0.7793 0.36438-0.7793 0.81445v5.5254h-8.1113v-5.5254c0-0.45012-0.34735-0.81445-0.77929-0.81445h-9.6719c-0.43195 0-0.7793 0.36438-0.7793 0.81445v20.48c0 0.45012 0.34707 0.8125 0.7793 0.8125h9.6719c0.43194 0 0.77929-0.36232 0.77929-0.8125v-5.5254h8.1113v5.5254c0 0.45012 0.34739 0.8125 0.7793 0.8125h4.8945v26.061c-2.8064 1.1139-4.7832 3.8423-4.7832 7.0547v39.494c0 4.2077 3.388 7.5957 7.5957 7.5957s7.5938-3.388 7.5938-7.5957v-39.494c0-3.2125-1.9766-5.9409-4.7832-7.0547v-26.061h5.3008c0.84434-0.74337 1.66-3.7262 6.1699-6.0059 12.111-4.1603 17.37 6.1474 17.277 4.7246-0.09864-1.519-0.77159-11.047-11.346-16.635-4.6068-2.0716-10.023-4.1068-11.484-4.1914-0.15078-0.079455-1.2532-0.11791-2.8223-0.13281z" fill="#e5b62f" stroke="#e5b62f" stroke-width="1.8045" style="paint-order:markers fill stroke"/>
+ <path id="path943" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m433.72 12.014c-4.1549 0.06908-7.4443 1.7901-9.4394 3.2734-2.6602 1.9777-3.5133 3.1091-7.5371 3.1191-4.0238 0.010205-4.8789-1.1215-7.5488-3.0859-2.6699-1.9645-7.6428-4.3321-13.879-2.6445-6.2361 1.6876-9.3374 6.2423-10.652 9.2852-1.3149 3.0428-1.4871 4.4482-4.9668 6.4688-3.4797 2.0206-4.7875 1.4757-8.082 1.1094-3.2945-0.36631-8.7909 0.065007-13.348 4.6445-4.5568 4.5795-4.9666 10.08-4.584 13.373 0.38259 3.2926 0.93677 4.5962-1.0664 8.0859-2.0032 3.4897-3.4032 3.6661-6.4394 4.9961-3.0363 1.33-7.5798 4.449-9.2363 10.693-1.6566 6.2444 0.74308 11.207 2.7207 13.867 1.9777 2.6602 3.1091 3.5133 3.1191 7.5371 0.00982 4.0238-1.1196 4.8769-3.084 7.5469-1.9645 2.67-4.3321 7.6448-2.6445 13.881 1.6876 6.236 6.2403 9.3374 9.2832 10.652 3.0428 1.3149 4.4501 1.4871 6.4707 4.9668 2.0205 3.4797 1.4757 4.7856 1.1094 8.0801-0.36631 3.2945 0.06278 8.7928 4.6426 13.35 4.5795 4.5568 10.08 4.9549 13.373 4.5723 3.2926-0.38272 4.5962-0.9251 8.0859 1.0781 3.4897 2.0032 3.668 3.4032 4.998 6.4394 1.33 3.0362 4.447 7.5798 10.691 9.2363 6.2444 1.6565 13.901-1.5082 13.867-2.7226-0.0344-1.2144-5.1602 0.87503-10.301-2.3594-5.1405-3.2344-4.7729-5.2312-5.9062-7.8184-1.1333-2.5872-3.1606-7.4448-8.8164-10.691-5.6557-3.2466-10.87-2.5429-13.676-2.2168-2.8057 0.32617-2.9932 0.8948-5.8887-1.9863-2.8955-2.8812-2.318-3.0658-2.0059-5.873 0.31218-2.8073 0.98369-8.0206-2.291-13.66s-8.1377-7.6472-10.73-8.7676c-2.5928-1.1204-3.0365-0.71142-4.1035-4.6543-1.067-3.943-0.48649-3.8284 1.1875-6.1035 1.6739-2.2751 4.8698-6.4494 4.8535-12.971-0.01587-6.5213-3.2327-10.692-4.918-12.959-1.6852-2.2667-2.272-2.1417-1.2246-6.0898 1.0474-3.9482 1.4987-3.5405 4.0859-4.6738 2.5872-1.1333 7.4467-3.1626 10.693-8.8184 3.2466-5.6557 2.5429-10.868 2.2168-13.674-0.32591-2.8057-0.89483-2.9932 1.9863-5.8887 2.8812-2.8955 3.0638-2.3297 5.8711-2.0176 2.8072 0.31211 8.0226 0.99541 13.662-2.2793 5.6395-3.2747 7.6354-8.1376 8.7559-10.73 1.1204-2.5928 0.72114-3.0365 4.6641-4.1035 3.9429-1.067 3.8284-0.48836 6.1035 1.1855 2.2751 1.6739 6.4494 4.8717 12.971 4.8555 6.5213-0.016252 10.682-3.2347 12.949-4.9199 2.2667-1.6852 2.1417-2.2701 6.0898-1.2227 3.9482 1.0474 3.5522 1.4987 4.6856 4.0859 1.1333 2.5872 3.1607 7.4448 8.8164 10.691s10.87 2.5429 13.676 2.2168c2.8057-0.3261 2.9932-0.89287 5.8887 1.9883s2.318 3.0638 2.0059 5.8711c-0.31212 2.8072-0.98369 8.0207 2.291 13.66s8.1376 7.6355 10.73 8.7559c2.5928 1.1204 3.0365 0.72311 4.1035 4.666 1.067 3.9429 0.48641 3.8284-1.1875 6.1035-1.6739 2.2751-4.8697 6.4494-4.8535 12.971 0.01625 6.5213 3.2327 10.681 4.918 12.947 1.6852 2.2667 2.27 2.1436 1.2227 6.0918-1.0474 3.9482-1.4968 3.5503-4.084 4.6836-2.5872 1.1333-7.4448 3.1626-10.691 8.8184-3.2466 5.6557-2.5448 10.868-2.2188 13.674 0.32609 2.8056 0.89482 2.9932-1.9863 5.8887-2.8812 2.8955-3.0638 2.32-5.8711 2.0078-2.8072-0.31219-8.0226-0.9856-13.662 2.2891-5.6395 3.2747-7.6452 8.1376-8.7656 10.73-0.9313 2.1552-0.84174 2.8278-3.0371 3.6055-0.31286-0.1736-0.67424-0.33971-1.1055-0.50195-3.6364-1.3682-4.115 0.48194-7.0996 2.7051-2.684 1.9992-4.605 2.234-4.4922 4.8711-0.25335-0.0254-0.49071-0.03001-0.68164-0.00781-0.60936 0.07067-0.85221 0.17155-1.498-0.19922s-0.67769-0.63142-0.92383-1.1934c-0.24616-0.56187-0.82291-1.4024-1.9785-1.709-1.1557-0.30652-2.5727 0.2791-2.5664 0.50391 0.00642 0.2245 0.95492-0.16129 1.9062 0.4375 0.95133 0.5986 0.88401 0.96652 1.0938 1.4453 0.20965 0.47887 0.58419 1.3796 1.6309 1.9805 1.0467 0.60083 2.012 0.47055 2.5312 0.41016 0.39726-0.04627 0.51372-0.11363 0.78515 0.09375 0.79696 2.7289 2.7246 2.4282 6.0664 3.6855 3.6364 1.3682 4.7478 3.0142 7.7324 0.79102 2.1873-1.6292 1.9673-2.8055 1.4394-4.7266 4.8594-2.0322 7.4092-5.8859 8.5684-8.5684 1.3149-3.0428 1.4871-4.4482 4.9668-6.4688 3.4797-2.0206 4.7876-1.4757 8.082-1.1094 3.2945 0.36624 8.7909-0.06484 13.348-4.6445 4.5568-4.5795 4.9667-10.08 4.584-13.373-0.38271-3.2926-0.93681-4.5962 1.0664-8.0859 2.0032-3.4897 3.4032-3.666 6.4394-4.9961 3.0363-1.33 7.5798-4.449 9.2363-10.693 1.6565-6.2444-0.74299-11.207-2.7207-13.867-1.9777-2.6601-3.1091-3.5133-3.1191-7.5371-0.00983-4.0238 1.1195-4.8769 3.084-7.5469 1.9645-2.6699 4.3321-7.6448 2.6445-13.881-1.6876-6.2361-6.2404-9.3375-9.2832-10.652-3.0428-1.3149-4.4502-1.4852-6.4707-4.9648s-1.4737-4.7876-1.1074-8.082c0.36631-3.2945-0.065-8.7928-4.6445-13.35-4.5795-4.5568-10.08-4.9647-13.373-4.582-3.2926 0.38271-4.5962 0.93681-8.0859-1.0664-3.4897-2.0032-3.6661-3.4032-4.9961-6.4395-1.33-3.0363-4.449-7.5817-10.693-9.2383-1.5611-0.41413-3.0428-0.57381-4.4277-0.55078z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <g id="text955" fill="#fff" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1476" d="m-30.49 126.82q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10528-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25148-1.0995-0.65504-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76615 0.11697 0.38601 0.11697 0.80125 0 1.1346-0.70182 1.8247-0.70182 0.69013-1.8364 0.69013z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1478" d="m-26.636 118.49h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1480" d="m-15.787 118.49v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1482" d="m-15.044 118.49h1.4972v5.2403q0 0.54976 0.04094 0.7486 0.08773 0.4211 0.3743 0.67843 0.29243 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67843 0.04094-0.19884 0.04094-0.7486v-5.2403h1.4972v5.2403q0 0.82463-0.093576 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052-0.95331 0-1.6434-0.52052-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1484" d="m-7.1892 119.89v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52637 0.66673 0.52637 1.6376t-0.52637 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text959" fill="#e5b62f" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1465" d="m2.1625 118.49v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1467" d="m4.3967 121.1q-0.087728 0.47957-0.087728 1.5089t0.087728 1.5089q0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261t0.73691-0.36261q0.28658-0.36846 0.39185-0.93576 0.087728-0.48543 0.087728-1.5089 0-1.0293-0.087728-1.5089-0.10527-0.56731-0.39185-0.92992-0.28073-0.36845-0.73691-0.36845t-0.74276 0.36845q-0.28073 0.36261-0.386 0.92992zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409t1.6668 0.61409q0.64918 0.60824 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825t-1.6668-0.60825q-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1469" d="m10.38 121.1q-0.08773 0.47957-0.08773 1.5089t0.08773 1.5089q0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92992-0.28073-0.36845-0.73691-0.36845-0.45618 0-0.74276 0.36845-0.28073 0.36261-0.386 0.92992zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60824 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1471" d="m15.246 126.73v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1473" d="m23.358 126.82q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10528-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25148-1.0995-0.65504-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76615 0.11697 0.38601 0.11697 0.80125 0 1.1346-0.70182 1.8247-0.70182 0.69013-1.8364 0.69013z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <path id="rect1036" d="m-34.068 133.32h61.237c0.87182 0 1.5737 0.70186 1.5737 1.5737v57.404c0 0.87182-0.70186 1.5737-1.5737 1.5737h-61.237c-0.87182 0-1.5737-0.70186-1.5737-1.5737v-57.404c0-0.87182 0.70186-1.5737 1.5737-1.5737z" style="paint-order:markers fill stroke"/>
+ <path id="rect1038" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m423.13 286.97c-4.7073-0.0447-13.613 0.13476-13.613 0.13476-0.43195 0-0.7793 0.36236-0.7793 0.8125v5.5274h-8.1113v-5.5274c0-0.4501-0.34735-0.8125-0.77929-0.8125h-9.6719c-0.43195 0-0.7793 0.36236-0.7793 0.8125v20.482c0 0.45015 0.34707 0.81055 0.7793 0.81055h9.6719c0.43194 0 0.77929-0.36052 0.77929-0.81055v-5.5273h8.1113v5.5273c0 0.45015 0.34739 0.81055 0.7793 0.81055h4.8945v26.061c-2.8064 1.1139-4.7832 3.8423-4.7832 7.0547v39.496c0 4.2077 3.388 7.5938 7.5957 7.5938s7.5938-3.386 7.5938-7.5938v-39.496c0-3.2125-1.9766-5.9409-4.7832-7.0547v-26.061h5.3008c0.84434-0.74336 1.66-3.7243 6.1699-6.0039 12.111-4.1603 17.37 6.1455 17.277 4.7227-0.09864-1.519-0.77159-11.045-11.346-16.633-4.6068-2.0716-10.023-4.1068-11.484-4.1914-0.15078-0.07946-1.2532-0.11986-2.8223-0.13476z" fill="#fff" stroke="#fff" stroke-width="1.8045" style="paint-order:markers fill stroke"/>
+ <path id="path1046" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m433.72 255.3c-4.1549 0.06908-7.4443 1.7902-9.4394 3.2734-2.6602 1.9777-3.5133 3.1091-7.5371 3.1191-4.0238 0.01133-4.8789-1.1195-7.5488-3.084-2.6699-1.9645-7.6428-4.3321-13.879-2.6445s-9.3374 6.2404-10.652 9.2832c-1.3149 3.0428-1.4871 4.4502-4.9668 6.4707-3.4797 2.0206-4.7875 1.4737-8.082 1.1074-3.2945-0.36624-8.7909 0.06484-13.348 4.6445-4.5568 4.5795-4.9666 10.08-4.584 13.373 0.38259 3.2926 0.93677 4.5962-1.0664 8.0859-2.0032 3.4897-3.4032 3.6661-6.4394 4.9961-3.0363 1.33-7.5798 4.449-9.2363 10.693-1.6566 6.2444 0.74308 11.207 2.7207 13.867 1.9777 2.6601 3.1091 3.5133 3.1191 7.5371 0.00982 4.0238-1.1196 4.8789-3.084 7.5488-1.9645 2.67-4.3321 7.6428-2.6445 13.879 1.6876 6.236 6.2403 9.3374 9.2832 10.652 3.0428 1.3149 4.4501 1.4871 6.4707 4.9668 2.0205 3.4797 1.4757 4.7875 1.1094 8.082-0.36631 3.2945 0.06278 8.7909 4.6426 13.348 4.5795 4.5568 10.08 4.9549 13.373 4.5723 3.2926-0.38271 4.5962-0.9251 8.0859 1.0781 3.4897 2.0032 3.668 3.4032 4.998 6.4394 1.33 3.0362 4.447 7.5798 10.691 9.2363 6.2444 1.6565 13.901-1.5063 13.867-2.7207-0.0344-1.2144-5.1602 0.87307-10.301-2.3613-5.1405-3.2344-4.7729-5.2312-5.9062-7.8184-1.1333-2.5872-3.1606-7.4448-8.8164-10.691-5.6557-3.2466-10.87-2.5429-13.676-2.2168-2.8057 0.32618-2.9932 0.89481-5.8887-1.9863-2.8955-2.8812-2.318-3.0638-2.0059-5.8711 0.31218-2.8073 0.98369-8.0226-2.291-13.662-3.2747-5.6395-8.1377-7.6452-10.73-8.7656-2.5928-1.1204-3.0365-0.71142-4.1035-4.6543-1.067-3.943-0.48649-3.8303 1.1875-6.1055 1.6739-2.2751 4.8698-6.4494 4.8535-12.971-0.01587-6.5214-3.2327-10.69-4.918-12.957-1.6852-2.2667-2.272-2.1436-1.2246-6.0918 1.0474-3.9482 1.4987-3.5405 4.0859-4.6738 2.5872-1.1333 7.4467-3.1607 10.693-8.8164 3.2466-5.6558 2.5429-10.87 2.2168-13.676-0.32591-2.8057-0.89483-2.9931 1.9863-5.8887 2.8812-2.8955 3.0638-2.3297 5.8711-2.0176 2.8072 0.31219 8.0226 0.9954 13.662-2.2793s7.6354-8.1377 8.7559-10.73c1.1204-2.5928 0.72114-3.0365 4.6641-4.1035 3.9429-1.067 3.8284-0.48641 6.1035 1.1875 2.2751 1.6739 6.4494 4.8698 12.971 4.8535 6.5213-0.01512 10.682-3.2327 12.949-4.918 2.2667-1.6852 2.1417-2.2701 6.0898-1.2226 3.9482 1.0474 3.5522 1.4967 4.6856 4.084 1.1333 2.5872 3.1607 7.4448 8.8164 10.691 5.6557 3.2466 10.87 2.5448 13.676 2.2188 2.8057-0.32618 2.9932-0.89485 5.8887 1.9863 2.8955 2.8811 2.318 3.0638 2.0059 5.8711-0.31212 2.8072-0.98369 8.0207 2.291 13.66 3.2747 5.6395 8.1376 7.6354 10.73 8.7559 2.5928 1.1204 3.0365 0.7231 4.1035 4.666 1.067 3.9429 0.48641 3.8284-1.1875 6.1035-1.6739 2.2751-4.8697 6.4494-4.8535 12.971 0.01625 6.5213 3.2327 10.682 4.918 12.949 1.6852 2.2667 2.27 2.1417 1.2227 6.0898-1.0474 3.9482-1.4968 3.5522-4.084 4.6856-2.5872 1.1333-7.4448 3.1607-10.691 8.8164-3.2466 5.6557-2.5448 10.868-2.2188 13.674 0.32609 2.8057 0.89482 2.9932-1.9863 5.8887-2.8812 2.8955-3.0638 2.32-5.8711 2.0078-2.8072-0.31219-8.0226-0.98365-13.662 2.291-5.6395 3.2747-7.6452 8.1376-8.7656 10.73-0.93126 2.1551-0.84199 2.8259-3.0371 3.6035-0.31271-0.17348-0.67455-0.33983-1.1055-0.50196-3.6364-1.3681-4.115 0.48194-7.0996 2.7051-2.684 1.9992-4.605 2.234-4.4922 4.8711-0.25326-0.02538-0.49076-0.03-0.68164-0.00782-0.60936 0.07068-0.85221 0.17156-1.498-0.19922-0.64583-0.37077-0.67769-0.62946-0.92383-1.1914-0.24616-0.56187-0.82291-1.4044-1.9785-1.7109-1.1557-0.30652-2.5727 0.2791-2.5664 0.50391 0.00642 0.2245 0.95492-0.16129 1.9062 0.4375 0.95133 0.5986 0.88401 0.96847 1.0938 1.4473 0.20965 0.47887 0.58419 1.3777 1.6309 1.9785 1.0467 0.60083 2.012 0.47055 2.5312 0.41015 0.39726-0.04626 0.51372-0.11362 0.78515 0.09375 0.79696 2.7289 2.7246 2.4282 6.0664 3.6856 3.6364 1.3682 4.7478 3.0142 7.7324 0.79102 2.1872-1.6292 1.9673-2.8037 1.4394-4.7246 4.8594-2.0322 7.4092-5.8859 8.5684-8.5684 1.3149-3.0428 1.4871-4.4502 4.9668-6.4707 3.4797-2.0206 4.7876-1.4757 8.082-1.1094 3.2945 0.36624 8.7909-0.06289 13.348-4.6426 4.5568-4.5795 4.9667-10.08 4.584-13.373-0.38271-3.2926-0.93681-4.5962 1.0664-8.0859 2.0032-3.4897 3.4032-3.668 6.4394-4.998 3.0363-1.33 7.5798-4.447 9.2363-10.691 1.6565-6.2444-0.74299-11.207-2.7207-13.867-1.9777-2.6602-3.1091-3.5133-3.1191-7.5371-0.00983-4.0238 1.1195-4.8789 3.084-7.5488 1.9645-2.6699 4.3321-7.6448 2.6445-13.881-1.6876-6.236-6.2404-9.3355-9.2832-10.65-3.0428-1.3149-4.4502-1.4871-6.4707-4.9668-2.0206-3.4797-1.4737-4.7876-1.1074-8.082 0.36631-3.2945-0.065-8.7909-4.6445-13.348-4.5795-4.5568-10.08-4.9667-13.373-4.584-3.2926 0.38271-4.5962 0.93682-8.0859-1.0664-3.4897-2.0032-3.6661-3.4032-4.9961-6.4394-1.33-3.0362-4.449-7.5798-10.693-9.2363-1.5611-0.41413-3.0428-0.57576-4.4277-0.55273z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <g id="text1058" fill="#fff" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1450" d="m-30.49 191.19q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21054 2.1639 0.21054 1.041 0 1.041-1.1112 0-0.30998-0.15206-0.58486-0.14621-0.28072-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78955 0 2.1639 0.14622v1.4095q-1.4212-0.15207-2.1639-0.15207-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.08773 0.15791-0.12867 0.32167-0.03509 0.16376-0.03509 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61995 0.30412 0.38015 0.42109 0.76615t0.11697 0.80125q0 1.1346-0.70182 1.8247t-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1452" d="m-26.636 182.86h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1454" d="m-15.787 182.86v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1456" d="m-15.044 182.86h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67843 0.29243 0.25733 0.70767 0.25733t0.70182-0.25733q0.29242-0.25734 0.38015-0.67843 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.093576 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52051-1.6376 0.52051-0.95331 0-1.6434-0.52051-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1458" d="m-7.1892 184.26v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75445 0-0.44449-0.23979-0.75446-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87142 0.52637 0.66673 0.52637 1.6376 0 0.97085-0.52637 1.6434-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1062" fill="#fff" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1439" d="m2.1625 182.86v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1441" d="m4.3967 185.47q-0.087728 0.47958-0.087728 1.5089t0.087728 1.5089q0.10527 0.56731 0.386 0.93576 0.28658 0.36261 0.74276 0.36261t0.73691-0.36261q0.28658-0.36845 0.39185-0.93576 0.087728-0.48542 0.087728-1.5089 0-1.0293-0.087728-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846t-0.74276 0.36846q-0.28073 0.36261-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64919-0.16376-2.0411 0-1.392 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.6141 1.6668-0.6141t1.6668 0.6141q0.64918 0.60824 0.88312 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.3919-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60824-1.6668 0.60824t-1.6668-0.60824q-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1443" d="m10.38 185.47q-0.08773 0.47958-0.08773 1.5089t0.08773 1.5089q0.10527 0.56731 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36845 0.39185-0.93576 0.08773-0.48542 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.36261-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64919-0.16376-2.0411 0-1.392 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64918-0.6141 1.6668-0.6141 1.0176 0 1.6668 0.6141 0.64918 0.60824 0.88312 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.3919-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60824-1.6668 0.60824-1.0176 0-1.6668-0.60824-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1445" d="m15.246 191.1v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1447" d="m23.358 191.19q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21054 2.1639 0.21054 1.041 0 1.041-1.1112 0-0.30998-0.15206-0.58486-0.14621-0.28072-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78955 0 2.1639 0.14622v1.4095q-1.4212-0.15207-2.1639-0.15207-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.08773 0.15791-0.12867 0.32167-0.03509 0.16376-0.03509 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61995 0.30412 0.38015 0.42109 0.76615t0.11697 0.80125q0 1.1346-0.70182 1.8247t-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <path id="rect830-6-9" d="m45.345 70.722h82.571c0.28796 0 0.51978 0.23182 0.51978 0.51978v22.171c0 0.28796-0.23182 0.51978-0.51978 0.51978h-82.571c-0.28796 0-0.51978-0.23182-0.51978-0.51978v-22.171c0-0.28796 0.23182-0.51978 0.51978-0.51978z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <path id="rect1070" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m645.59 29.135c-2.1498-0.02041-6.2168 0.060546-6.2168 0.060546-0.19725 0-0.35547 0.16556-0.35547 0.37109v2.5234h-3.7051v-2.5234c0-0.20557-0.15821-0.37109-0.35547-0.37109h-4.416c-0.19725 0-0.35742 0.16556-0.35742 0.37109v9.3535c0 0.20557 0.16001 0.37109 0.35742 0.37109h4.416c0.19726 0 0.35547-0.16549 0.35547-0.37109v-2.5234h3.7051v2.5234c0 0.20557 0.15825 0.37109 0.35547 0.37109h2.2363v11.906c-1.2817 0.50868-2.1856 1.7497-2.1856 3.2168v18.037c0 1.9217 1.5471 3.4688 3.4688 3.4688s3.4688-1.5471 3.4688-3.4688v-18.037c0-1.4664-0.90281-2.7077-2.1836-3.2168v-11.906h2.4199c0.38561-0.33948 0.75677-1.7011 2.8164-2.7422 5.5309-1.9 7.9328 2.808 7.8906 2.1582-0.04505-0.69374-0.35053-5.0458-5.1797-7.5977-2.1039-0.94608-4.5788-1.8754-5.2461-1.9141-0.06886-0.036283-0.57246-0.053743-1.2891-0.060546z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".82412" style="paint-order:markers fill stroke"/>
+ <path id="path1078" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m650.43 14.668c-1.8975 0.031546-3.3994 0.81867-4.3105 1.4961-1.2149 0.90321-1.6057 1.4193-3.4434 1.4238-1.8376 0.004535-2.2279-0.51103-3.4473-1.4082-1.2194-0.89717-3.4899-1.9797-6.3379-1.209-2.848 0.77071-4.2647 2.8506-4.8652 4.2402-0.60051 1.3896-0.67842 2.0323-2.2676 2.9551-1.5892 0.92278-2.1868 0.67314-3.6914 0.50586-1.5046-0.16728-4.0146 0.029711-6.0957 2.1211-2.0811 2.0914-2.2685 4.6037-2.0938 6.1074 0.17473 1.5037 0.42851 2.0996-0.48633 3.6934-0.91487 1.5937-1.5548 1.6738-2.9414 2.2812-1.3867 0.60742-3.4622 2.033-4.2188 4.8848-0.75654 2.8518 0.34098 5.1172 1.2441 6.332 0.90319 1.2149 1.4192 1.6057 1.4238 3.4434 0.00454 1.8377-0.51104 2.2279-1.4082 3.4473-0.89718 1.2194-1.9797 3.4899-1.209 6.3379 0.77071 2.848 2.8506 4.2647 4.2402 4.8652 1.3896 0.60051 2.0323 0.67843 2.9551 2.2676 0.92276 1.5892 0.67316 2.1868 0.50586 3.6914-0.16728 1.5046 0.0297 4.0146 2.1211 6.0957 2.0915 2.0811 4.6037 2.2627 6.1074 2.0879s2.0996-0.42268 3.6934 0.49219c1.5937 0.91486 1.6738 1.5547 2.2812 2.9414 0.60742 1.3866 2.031 3.4622 4.8828 4.2188 2.8518 0.75653 6.3494-0.68953 6.334-1.2441-0.01587-0.55461-2.3574 0.40097-4.7051-1.0762-2.3477-1.4771-2.1797-2.3907-2.6973-3.5723-0.51759-1.1816-1.4424-3.3982-4.0254-4.8809-2.5829-1.4827-4.9648-1.1626-6.2461-1.0137-1.2813 0.14895-1.3671 0.4076-2.6894-0.9082-1.3224-1.3158-1.0586-1.3996-0.91602-2.6816 0.14257-1.2821 0.44867-3.6627-1.0469-6.2383-1.4956-2.5755-3.7163-3.4922-4.9004-4.0039-1.1841-0.5117-1.3857-0.3243-1.873-2.125-0.4873-1.8007-0.22347-1.7481 0.54102-2.7871 0.76447-1.039 2.2242-2.9456 2.2168-5.9238-0.00718-2.9783-1.4764-4.8828-2.2461-5.918-0.76963-1.0352-1.037-0.98009-0.55859-2.7832 0.47833-1.8031 0.68366-1.6152 1.8652-2.1328 1.1816-0.51758 3.4001-1.4444 4.8828-4.0273 1.4827-2.583 1.1626-4.9648 1.0137-6.2461-0.14883-1.2813-0.4076-1.3671 0.90821-2.6895 1.3158-1.3224 1.3976-1.0625 2.6797-0.91992 1.282 0.14253 3.6647 0.45453 6.2402-1.041 2.5755-1.4955 3.4864-3.7163 3.998-4.9004 0.5117-1.1841 0.33013-1.3877 2.1309-1.875 1.8007-0.48731 1.7481-0.22346 2.7871 0.54102 1.039 0.76448 2.9456 2.2262 5.9238 2.2188 2.9783-0.007559 4.8789-1.4784 5.9141-2.248 1.0352-0.76964 0.97813-1.0369 2.7812-0.55859 1.8031 0.47834 1.6211 0.68562 2.1387 1.8672s1.4444 3.4001 4.0273 4.8828c2.583 1.4827 4.9648 1.1607 6.2461 1.0117 1.2813-0.14891 1.3651-0.40761 2.6875 0.9082 1.3224 1.3158 1.0605 1.3996 0.91796 2.6816-0.14252 1.282-0.45062 3.6627 1.0449 6.2383 1.4955 2.5755 3.7162 3.4864 4.9004 3.998 1.1841 0.5117 1.3877 0.33017 1.875 2.1309 0.4873 1.8007 0.22346 1.75-0.54102 2.7891-0.76448 1.039-2.2262 2.9436-2.2188 5.9219 0.00756 2.9783 1.4765 4.8789 2.2461 5.9141 0.76965 1.0352 1.0369 0.97814 0.5586 2.7812-0.47834 1.8031-0.68366 1.623-1.8652 2.1406-1.1816 0.51757-3.4001 1.4424-4.8828 4.0254-1.4827 2.5829-1.1606 4.9647-1.0117 6.2461 0.14892 1.2813 0.40761 1.3671-0.9082 2.6895-1.3158 1.3224-1.3996 1.0586-2.6816 0.91602-1.2821-0.14256-3.6627-0.44865-6.2383 1.0469-2.5756 1.4956-3.4922 3.7163-4.0039 4.9004-0.42128 0.9749-0.43101 1.2878-1.4043 1.6387-0.13992-0.076625-0.29747-0.15087-0.48828-0.22266-1.6607-0.62483-1.8791 0.22103-3.2422 1.2363-1.2238 0.91157-2.0954 1.0256-2.0469 2.2246-0.11719-0.012065-0.22826-0.014151-0.3164-0.003906-0.27829 0.032126-0.38865 0.077526-0.6836-0.091797s-0.30944-0.28632-0.42187-0.54297c-0.11241-0.25659-0.3746-0.64124-0.90235-0.78125-0.52779-0.13999-1.1767 0.12778-1.1738 0.23047 0.00294 0.10254 0.43663-0.07423 0.8711 0.19922 0.43447 0.27337 0.40421 0.4415 0.5 0.66016 0.09573 0.21868 0.26613 0.6299 0.74414 0.9043 0.47801 0.27439 0.91911 0.21509 1.1562 0.1875 0.17999-0.020943 0.25507-0.013849 0.37695 0.078125 0.37226 1.1966 1.2449 1.0814 2.752 1.6484 1.6608 0.62483 2.1701 1.3747 3.5332 0.35938 0.9978-0.74322 0.89282-1.2826 0.65234-2.1582 2.2207-0.92784 3.3884-2.6867 3.918-3.9121 0.60051-1.3896 0.67842-2.0323 2.2676-2.9551 1.5892-0.92279 2.1868-0.67315 3.6914-0.50586 1.5046 0.16724 4.0146-0.029704 6.0957-2.1211 2.0811-2.0914 2.2685-4.6037 2.0938-6.1074-0.17477-1.5037-0.42853-2.0996 0.48633-3.6934 0.91486-1.5937 1.5548-1.6738 2.9414-2.2812 1.3866-0.60742 3.4622-2.031 4.2188-4.8828 0.75654-2.8518-0.34091-5.1191-1.2441-6.334-0.90322-1.2149-1.4193-1.6038-1.4238-3.4414-0.00453-1.8376 0.51104-2.2279 1.4082-3.4473 0.89716-1.2194 1.9797-3.4919 1.209-6.3398-0.77071-2.848-2.8506-4.2647-4.2402-4.8652-1.3896-0.60051-2.0323-0.67842-2.9551-2.2676-0.92278-1.5892-0.67315-2.1868-0.50586-3.6914 0.16728-1.5046-0.02971-4.0146-2.1211-6.0957-2.0914-2.0811-4.6037-2.2685-6.1074-2.0938-1.5037 0.17477-2.0996 0.42853-3.6934-0.48633-1.5937-0.91486-1.6738-1.5548-2.2812-2.9414-0.60741-1.3866-2.033-3.4622-4.8848-4.2188-0.71295-0.18913-1.389-0.26247-2.0215-0.25195z" fill="#336790" style="paint-order:markers fill stroke"/>
+ <g id="text1090" fill="#336790" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1424" d="m70.306 86.538q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.06433-0.46788-0.10527-0.29827-0.04094-0.61409-0.11697-0.30997-0.08188-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.09942-0.16376 0.09943-0.25149 0.25733-0.08773 0.15791-0.12867 0.32167-0.03509 0.16376-0.03509 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.07018 0.69012 0.15791 0.54391 0.08188 0.87728 0.25733 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247t-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1426" d="m74.16 78.209h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1428" d="m85.009 78.209v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1430" d="m85.752 78.209h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67843 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29243-0.25733 0.38015-0.67843 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052-0.95331 0-1.6434-0.52052-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1432" d="m93.607 79.613v2.3862h0.36261q0.50297 0 0.73106-0.04679 0.3977-0.08188 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.04679-0.73106-0.04679zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.08773 0.83049 0.19885 1.351 0.87143 0.52637 0.66673 0.52637 1.6376t-0.52637 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.08773-1.234 0.08773h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1094" fill="#e5b62f" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1413" d="m102.96 78.209v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1415" d="m105.19 80.818q-0.0877 0.47958-0.0877 1.5089t0.0877 1.5089q0.10528 0.5673 0.38601 0.93576 0.28657 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.0877-0.48543 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45619 0-0.74276 0.36846-0.28073 0.36261-0.38601 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411t0.16376-2.0411q0.23394-0.94746 0.88312-1.5557 0.64919-0.61409 1.6668-0.61409t1.6668 0.61409q0.64919 0.60824 0.88313 1.5557 0.16375 0.64918 0.16375 2.0411t-0.16375 2.0411q-0.23394 0.94746-0.88313 1.5616-0.64918 0.60824-1.6668 0.60824t-1.6668-0.60824q-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1417" d="m111.18 80.818q-0.0877 0.47958-0.0877 1.5089t0.0877 1.5089q0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261t0.73691-0.36261q0.28658-0.36846 0.39185-0.93576 0.0877-0.48543 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846t-0.74276 0.36846q-0.28073 0.36261-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411t0.16376-2.0411q0.23394-0.94746 0.88313-1.5557 0.64918-0.61409 1.6668-0.61409t1.6668 0.61409q0.64919 0.60824 0.88313 1.5557 0.16376 0.64918 0.16376 2.0411t-0.16376 2.0411q-0.23394 0.94746-0.88313 1.5616-0.64918 0.60824-1.6668 0.60824t-1.6668-0.60824q-0.64919-0.61409-0.88313-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1419" d="m116.04 86.444v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1421" d="m124.15 86.538q-0.95916 0-2.164-0.18715v-1.427q1.4095 0.21055 2.164 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.06433-0.46789-0.10527-0.29827-0.04094-0.61409-0.11697-0.30997-0.08188-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78954 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22225 0-0.39185 0.09942-0.16376 0.09943-0.25149 0.25733-0.0877 0.15791-0.12867 0.32167-0.0351 0.16376-0.0351 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.07018 0.69013 0.15791 0.54391 0.08188 0.87727 0.25733 0.46788 0.23979 0.77201 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247t-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <path id="rect830-6" d="m45.345 102.91h82.571c0.28796 0 0.51978 0.23183 0.51978 0.51978v22.171c0 0.28796-0.23182 0.51978-0.51978 0.51978h-82.571c-0.28796 0-0.51978-0.23182-0.51978-0.51978v-22.171c0-0.28795 0.23182-0.51978 0.51978-0.51978z" fill="#336790" style="paint-order:markers fill stroke"/>
+ <path id="rect1168" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m645.59 150.78c-2.1498-0.02041-6.2168 0.06055-6.2168 0.06055-0.19725 0-0.35547 0.16549-0.35547 0.37109v2.5254h-3.7051v-2.5254c0-0.2056-0.15821-0.37109-0.35547-0.37109h-4.416c-0.19725 0-0.35742 0.16549-0.35742 0.37109v9.3535c0 0.20561 0.16001 0.37109 0.35742 0.37109h4.416c0.19726 0 0.35547-0.16548 0.35547-0.37109v-2.5234h3.7051v2.5234c0 0.20561 0.15825 0.37109 0.35547 0.37109h2.2363v11.906c-1.2817 0.50867-2.1856 1.7516-2.1856 3.2188v18.037c0 1.9216 1.5471 3.4688 3.4688 3.4688s3.4688-1.5471 3.4688-3.4688v-18.037c0-1.4665-0.90278-2.7096-2.1836-3.2188v-11.906h2.4199c0.38561-0.3394 0.75677-1.7011 2.8164-2.7422 5.5309-1.9 7.9328 2.808 7.8906 2.1582-0.04505-0.69373-0.35053-5.0458-5.1797-7.5977-2.1039-0.94609-4.5788-1.8754-5.2461-1.9141-0.06886-0.03628-0.57246-0.05374-1.2891-0.06055z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".82412" style="paint-order:markers fill stroke"/>
+ <path id="path1176" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m650.43 136.31c-1.8975 0.03152-3.3994 0.81663-4.3105 1.4941-1.2149 0.90323-1.6057 1.4193-3.4434 1.4238-1.8376 0.00378-2.2279-0.51106-3.4473-1.4082-1.2194-0.89719-3.4899-1.9778-6.3379-1.207-2.848 0.77069-4.2647 2.8506-4.8652 4.2402-0.60051 1.3896-0.67842 2.0304-2.2676 2.9531-1.5892 0.92281-2.1868 0.6751-3.6914 0.50782-1.5046-0.16744-4.0146 0.02853-6.0957 2.1191-2.0811 2.0914-2.2685 4.6056-2.0938 6.1094 0.17473 1.5038 0.42851 2.0977-0.48633 3.6914-0.91487 1.5937-1.5548 1.6758-2.9414 2.2832-1.3867 0.60741-3.4622 2.031-4.2188 4.8828-0.75654 2.8518 0.34098 5.1171 1.2441 6.332 0.90319 1.2149 1.4192 1.6057 1.4238 3.4434 0.00454 1.8376-0.51104 2.2279-1.4082 3.4473-0.89718 1.2194-1.9797 3.4899-1.209 6.3379 0.77071 2.848 2.8506 4.2647 4.2402 4.8652 1.3896 0.6005 2.0323 0.67844 2.9551 2.2676 0.92276 1.5892 0.67316 2.1868 0.50586 3.6914-0.16728 1.5046 0.0297 4.0146 2.1211 6.0957 2.0915 2.081 4.6037 2.2627 6.1074 2.0879 1.5037-0.17499 2.0996-0.42268 3.6934 0.49219 1.5937 0.91484 1.6738 1.5548 2.2812 2.9414 0.60742 1.3866 2.031 3.4622 4.8828 4.2188 2.8518 0.75654 6.3494-0.68758 6.334-1.2422-0.01587-0.55461-2.3574 0.39899-4.7051-1.0781-2.3477-1.4772-2.1797-2.3888-2.6973-3.5703-0.51759-1.1816-1.4424-3.4001-4.0254-4.8828-2.5829-1.4827-4.9648-1.1626-6.2461-1.0137-1.2813 0.14891-1.3671 0.40955-2.6894-0.90625-1.3224-1.3158-1.0586-1.3996-0.91602-2.6816 0.14257-1.2821 0.44867-3.6647-1.0469-6.2402-1.4956-2.5756-3.7163-3.4922-4.9004-4.0039-1.1841-0.51171-1.3857-0.32447-1.873-2.125-0.4873-1.8008-0.22347-1.7481 0.54102-2.7871 0.76447-1.039 2.2242-2.9456 2.2168-5.9238-0.00718-2.9783-1.4764-4.8828-2.2461-5.918-0.76963-1.0352-1.037-0.97811-0.55859-2.7812 0.47833-1.8031 0.68366-1.6172 1.8652-2.1348 1.1816-0.51756 3.4001-1.4444 4.8828-4.0273 1.4827-2.583 1.1626-4.9628 1.0137-6.2441-0.14883-1.2813-0.4076-1.3671 0.90821-2.6894s1.3976-1.0644 2.6797-0.92188c1.282 0.14249 3.6647 0.45455 6.2402-1.041 2.5755-1.4955 3.4864-3.7163 3.998-4.9004 0.5117-1.1842 0.33013-1.3877 2.1309-1.875 1.8007-0.48734 1.7481-0.22152 2.7871 0.54296 1.039 0.76449 2.9456 2.2242 5.9238 2.2168 2.9783-0.00756 4.8789-1.4765 5.9141-2.2461 1.0352-0.76967 0.97813-1.0369 2.7812-0.5586 1.8031 0.47834 1.6211 0.68368 2.1387 1.8652 0.51758 1.1816 1.4444 3.4001 4.0273 4.8828s4.9648 1.1607 6.2461 1.0117c1.2813-0.14891 1.3651-0.4076 2.6875 0.9082 1.3224 1.3158 1.0605 1.3996 0.91796 2.6816-0.14252 1.2821-0.45062 3.6627 1.0449 6.2383 1.4955 2.5755 3.7162 3.4883 4.9004 4 1.1841 0.51171 1.3877 0.32819 1.875 2.1289 0.4873 1.8007 0.22346 1.7501-0.54102 2.7891-0.76448 1.039-2.2262 2.9456-2.2188 5.9238 0.00756 2.9783 1.4765 4.8769 2.2461 5.9121 0.76965 1.0352 1.0369 0.97811 0.5586 2.7812-0.47834 1.8031-0.68366 1.6231-1.8652 2.1406-1.1816 0.51756-3.4001 1.4444-4.8828 4.0273-1.4827 2.583-1.1606 4.9628-1.0117 6.2441s0.40761 1.3671-0.9082 2.6894-1.3996 1.0585-2.6816 0.91602c-1.2821-0.14249-3.6627-0.44865-6.2383 1.0469-2.5756 1.4956-3.4922 3.7163-4.0039 4.9004-0.42101 0.9743-0.43101 1.288-1.4023 1.6387-0.14024-0.0769-0.29879-0.15063-0.49024-0.22266-1.6607-0.62483-1.8791 0.22103-3.2422 1.2363-1.2238 0.91157-2.0954 1.0256-2.0469 2.2246-0.11719-0.01206-0.22826-0.01416-0.3164-0.00391-0.27829 0.03402-0.38865 0.07753-0.6836-0.0918-0.29495-0.16932-0.30944-0.28633-0.42187-0.54296-0.11241-0.25663-0.3746-0.64126-0.90235-0.78125-0.52779-0.13985-1.1767 0.12766-1.1738 0.23046 0.00294 0.10243 0.43663-0.07441 0.8711 0.19922 0.43447 0.27326 0.40421 0.44148 0.5 0.66016 0.09573 0.21883 0.26613 0.62986 0.74414 0.9043 0.47801 0.27439 0.91911 0.21509 1.1562 0.1875 0.17999-0.02009 0.25507-0.01352 0.37695 0.07812 0.37226 1.1966 1.2449 1.0814 2.752 1.6484 1.6608 0.62483 2.1701 1.3747 3.5332 0.35937 0.99763-0.74309 0.89468-1.2829 0.65429-2.1582 2.2192-0.92808 3.3866-2.687 3.916-3.9121 0.60051-1.3897 0.67842-2.0323 2.2676-2.9551s2.1868-0.67314 3.6914-0.50586c1.5046 0.16744 4.0146-0.03044 6.0957-2.1211 2.0811-2.0914 2.2685-4.6037 2.0938-6.1074s-0.42853-2.0996 0.48633-3.6934c0.91486-1.5938 1.5548-1.6738 2.9414-2.2812 1.3866-0.60741 3.4622-2.031 4.2188-4.8828 0.75654-2.8518-0.34091-5.1191-1.2441-6.334-0.90322-1.2149-1.4193-1.6038-1.4238-3.4414-0.00453-1.8376 0.51104-2.2279 1.4082-3.4473 0.89716-1.2194 1.9797-3.4919 1.209-6.3398-0.77071-2.848-2.8506-4.2628-4.2402-4.8633-1.3896-0.60053-2.0323-0.68036-2.9551-2.2695-0.92278-1.5891-0.67315-2.1849-0.50586-3.6894 0.16728-1.5046-0.02971-4.0166-2.1211-6.0977-2.0914-2.0811-4.6037-2.2666-6.1074-2.0918-1.5037 0.17499-2.0996 0.42659-3.6934-0.48828-1.5937-0.91488-1.6738-1.5548-2.2812-2.9414-0.60741-1.3867-2.033-3.4602-4.8848-4.2168-0.71295-0.18912-1.389-0.26246-2.0215-0.25195z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <g id="text1188" fill="#fff" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1385" d="m70.306 118.72q-0.95916 0-2.1639-0.18716v-1.427q1.4095 0.21054 2.1639 0.21054 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10528-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25148-1.0995-0.65504-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78955 0 2.1639 0.14622v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32166-0.03509 0.16376-0.03509 0.32167 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23978 0.772 0.61994 0.30412 0.38015 0.42109 0.76615 0.11697 0.386 0.11697 0.80125 0 1.1346-0.70182 1.8247-0.70182 0.69013-1.8364 0.69013z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1387" d="m74.16 110.39h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1389" d="m85.009 110.39v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1391" d="m85.752 110.39h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.4211 0.3743 0.67843 0.29242 0.25733 0.70767 0.25733t0.70182-0.25733q0.29243-0.25733 0.38015-0.67843 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052-0.95331 0-1.6434-0.52052-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1393" d="m93.607 111.8v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52637 0.66673 0.52637 1.6376t-0.52637 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1192" fill="#e5b62f" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1396" d="m102.96 110.39v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1398" d="m105.19 113q-0.0877 0.47957-0.0877 1.5089t0.0877 1.5089q0.10528 0.5673 0.38601 0.93576 0.28657 0.3626 0.74276 0.3626 0.45618 0 0.73691-0.3626 0.28658-0.36846 0.39185-0.93576 0.0877-0.48543 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.56731-0.39185-0.92992-0.28073-0.36845-0.73691-0.36845-0.45619 0-0.74276 0.36845-0.28073 0.36261-0.38601 0.92992zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64919-0.6141 1.6668-0.6141t1.6668 0.6141q0.64919 0.60824 0.88313 1.5557 0.16375 0.64919 0.16375 2.0411 0 1.392-0.16375 2.0411-0.23394 0.94746-0.88313 1.5616-0.64918 0.60825-1.6668 0.60825t-1.6668-0.60825q-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1400" d="m111.18 113q-0.0877 0.47957-0.0877 1.5089t0.0877 1.5089q0.10527 0.5673 0.386 0.93576 0.28658 0.3626 0.74276 0.3626t0.73691-0.3626q0.28658-0.36846 0.39185-0.93576 0.0877-0.48543 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.56731-0.39185-0.92992-0.28073-0.36845-0.73691-0.36845t-0.74276 0.36845q-0.28073 0.36261-0.386 0.92992zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88313-1.5557 0.64918-0.6141 1.6668-0.6141t1.6668 0.6141q0.64919 0.60824 0.88313 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88313 1.5616-0.64918 0.60825-1.6668 0.60825t-1.6668-0.60825q-0.64919-0.61409-0.88313-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1402" d="m116.04 118.63v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1404" d="m124.15 118.72q-0.95916 0-2.164-0.18716v-1.427q1.4095 0.21054 2.164 0.21054 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46789-0.10528-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25148-1.0995-0.65504-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78954 0 2.1639 0.14622v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22225 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.0877 0.15791-0.12867 0.32166-0.0351 0.16376-0.0351 0.32167 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69013 0.15791 0.54391 0.0819 0.87727 0.25734 0.46788 0.23978 0.77201 0.61994 0.30412 0.38015 0.42109 0.76615t0.11697 0.80125q0 1.1346-0.70182 1.8247-0.70182 0.69013-1.8364 0.69013z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <path id="rect830-6-9-8" d="m45.345 135.09h82.571c0.28796 0 0.51978 0.23182 0.51978 0.51978v22.171c0 0.28796-0.23182 0.51978-0.51978 0.51978h-82.571c-0.28796 0-0.51978-0.23182-0.51978-0.51978v-22.171c0-0.28796 0.23182-0.51978 0.51978-0.51978z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <path id="rect1222" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m645.59 272.42c-2.1498-0.02041-6.2168 0.06055-6.2168 0.06055-0.19725 0-0.35547 0.16744-0.35547 0.37305v2.5234h-3.7051v-2.5234c0-0.20561-0.15821-0.37305-0.35547-0.37305h-4.416c-0.19725 0-0.35742 0.16744-0.35742 0.37305v9.3535c0 0.20561 0.16001 0.36914 0.35742 0.36914h4.416c0.19726 0 0.35547-0.16353 0.35547-0.36914v-2.5254h3.7051v2.5254c0 0.20561 0.15825 0.36914 0.35547 0.36914h2.2363v11.906c-1.2817 0.50867-2.1856 1.7516-2.1856 3.2188v18.037c0 1.9217 1.5471 3.4688 3.4688 3.4688s3.4688-1.5471 3.4688-3.4688v-18.037c0-1.4665-0.90278-2.7096-2.1836-3.2188v-11.906h2.4199c0.38561-0.3394 0.75677-1.7011 2.8164-2.7422 5.5309-1.9 7.9328 2.808 7.8906 2.1582-0.04505-0.69373-0.35053-5.0458-5.1797-7.5977-2.1039-0.94609-4.5788-1.8754-5.2461-1.9141-0.06886-0.03628-0.57246-0.05374-1.2891-0.06055z" stroke="#000" stroke-width=".82412" style="paint-order:markers fill stroke"/>
+ <path id="path1230" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m650.43 257.96c-1.8975 0.03152-3.3994 0.81663-4.3105 1.4941-1.2149 0.90323-1.6057 1.4192-3.4434 1.4238-1.8376 0.00378-2.2279-0.51106-3.4473-1.4082-1.2194-0.89718-3.4899-1.9778-6.3379-1.207-2.848 0.77069-4.2647 2.8506-4.8652 4.2402-0.60051 1.3896-0.67842 2.0323-2.2676 2.9551-1.5892 0.92281-2.1868 0.67315-3.6914 0.50586-1.5046-0.16743-4.0146 0.03049-6.0957 2.1211-2.0811 2.0914-2.2685 4.6037-2.0938 6.1074 0.17473 1.5038 0.42851 2.0977-0.48633 3.6914-0.91487 1.5937-1.5548 1.6758-2.9414 2.2832-1.3867 0.60741-3.4622 2.031-4.2188 4.8828-0.75654 2.8518 0.34098 5.1191 1.2441 6.334 0.90319 1.2149 1.4192 1.6038 1.4238 3.4414 0.00454 1.8376-0.51104 2.2279-1.4082 3.4473-0.89718 1.2194-1.9797 3.4918-1.209 6.3398 0.77071 2.848 2.8506 4.2628 4.2402 4.8633 1.3896 0.60049 2.0323 0.67844 2.9551 2.2676 0.92276 1.5892 0.67316 2.1868 0.50586 3.6914-0.16728 1.5046 0.0297 4.0146 2.1211 6.0957 2.0915 2.081 4.6037 2.2627 6.1074 2.0879 1.5037-0.17499 2.0996-0.42073 3.6934 0.49414 1.5937 0.91484 1.6738 1.5528 2.2812 2.9395 0.60742 1.3866 2.031 3.4622 4.8828 4.2187 2.8518 0.75655 6.3494-0.68757 6.334-1.2422-0.01587-0.55461-2.3574 0.39899-4.7051-1.0781-2.3477-1.4772-2.1797-2.3888-2.6973-3.5703-0.51759-1.1816-1.4424-3.4001-4.0254-4.8828-2.5829-1.4827-4.9648-1.1626-6.2461-1.0137-1.2813 0.14891-1.3671 0.40955-2.6894-0.90625-1.3224-1.3158-1.0586-1.3996-0.91602-2.6816 0.14257-1.282 0.44867-3.6628-1.0469-6.2383-1.4956-2.5756-3.7163-3.4922-4.9004-4.0039-1.1841-0.51171-1.3857-0.32643-1.873-2.127-0.4873-1.8008-0.22347-1.7481 0.54102-2.7871 0.76447-1.039 2.2242-2.9456 2.2168-5.9238-0.00718-2.9783-1.4764-4.8828-2.2461-5.918-0.76963-1.0352-1.037-0.97811-0.55859-2.7812 0.47833-1.8031 0.68366-1.6172 1.8652-2.1348 1.1816-0.51757 3.4001-1.4444 4.8828-4.0273 1.4827-2.583 1.1626-4.9628 1.0137-6.2441-0.14883-1.2813-0.4076-1.3671 0.90821-2.6895 1.3158-1.3224 1.3976-1.0644 2.6797-0.92187 1.282 0.14249 3.6647 0.45454 6.2402-1.041 2.5755-1.4955 3.4864-3.7163 3.998-4.9004 0.5117-1.1842 0.33013-1.3877 2.1309-1.875 1.8007-0.48733 1.7481-0.22151 2.7871 0.54297 1.039 0.76449 2.9456 2.2242 5.9238 2.2168 2.9783-0.00756 4.8789-1.4765 5.9141-2.2461 1.0352-0.76967 0.97813-1.0369 2.7812-0.5586 1.8031 0.47834 1.6211 0.68368 2.1387 1.8652 0.51758 1.1816 1.4444 3.4001 4.0273 4.8828s4.9648 1.1626 6.2461 1.0137c1.2813-0.14891 1.3651-0.40955 2.6875 0.90625 1.3224 1.3158 1.0605 1.3996 0.91796 2.6816-0.14252 1.282-0.45062 3.6627 1.0449 6.2383 1.4955 2.5755 3.7162 3.4883 4.9004 4 1.1841 0.51171 1.3877 0.33014 1.875 2.1309 0.4873 1.8007 0.22346 1.7481-0.54102 2.7871-0.76448 1.039-2.2262 2.9456-2.2188 5.9238 0.00756 2.9783 1.4765 4.8769 2.2461 5.9121 0.76965 1.0352 1.0369 0.98006 0.5586 2.7832-0.47834 1.8031-0.68366 1.6211-1.8652 2.1387-1.1816 0.51757-3.4001 1.4444-4.8828 4.0274-1.4827 2.583-1.1606 4.9628-1.0117 6.2441 0.14892 1.2813 0.40761 1.3671-0.9082 2.6894s-1.3996 1.0585-2.6816 0.91602c-1.2821-0.14249-3.6627-0.44865-6.2383 1.0469-2.5756 1.4956-3.4922 3.7163-4.0039 4.9004-0.42099 0.97425-0.43116 1.2881-1.4023 1.6387-0.14024-0.07689-0.29879-0.15062-0.49024-0.22265-1.6607-0.62483-1.8791 0.22103-3.2422 1.2363-1.2238 0.91157-2.0954 1.0256-2.0469 2.2246-0.11719-0.01207-0.22826-0.01416-0.3164-0.00391-0.27829 0.03402-0.38865 0.07948-0.6836-0.08984-0.29495-0.16933-0.30944-0.2883-0.42187-0.54493-0.11241-0.25663-0.3746-0.64125-0.90235-0.78125-0.52779-0.13984-1.1767 0.12767-1.1738 0.23047 0.00294 0.10243 0.43663-0.07442 0.8711 0.19922 0.43447 0.27326 0.40421 0.44147 0.5 0.66016 0.09573 0.21883 0.26613 0.62986 0.74414 0.90429 0.47801 0.2744 0.91911 0.21509 1.1562 0.1875 0.17999-0.02008 0.25507-0.01351 0.37695 0.07813 0.37226 1.1966 1.2449 1.0814 2.752 1.6484 1.6608 0.62483 2.1701 1.3766 3.5332 0.36133 0.99774-0.74318 0.89279-1.2827 0.65234-2.1582 2.2207-0.92783 3.3884-2.6886 3.918-3.9141 0.60051-1.3897 0.67842-2.0323 2.2676-2.9551s2.1868-0.67314 3.6914-0.50586c1.5046 0.16743 4.0146-0.03045 6.0957-2.1211 2.0811-2.0914 2.2685-4.6037 2.0938-6.1074-0.17477-1.5037-0.42853-2.0977 0.48633-3.6914 0.91486-1.5938 1.5548-1.6758 2.9414-2.2832 1.3866-0.60741 3.4622-2.031 4.2188-4.8828 0.75654-2.8518-0.34091-5.1191-1.2441-6.334-0.90322-1.2149-1.4193-1.6038-1.4238-3.4414-0.00453-1.8376 0.51104-2.2279 1.4082-3.4473 0.89716-1.2194 1.9797-3.4919 1.209-6.3398-0.77071-2.848-2.8506-4.2628-4.2402-4.8633-1.3896-0.60053-2.0323-0.68035-2.9551-2.2695-0.92278-1.5891-0.67315-2.1849-0.50586-3.6894 0.16728-1.5046-0.02971-4.0166-2.1211-6.0977-2.0914-2.0811-4.6037-2.2666-6.1074-2.0918-1.5037 0.17499-2.0996 0.42659-3.6934-0.48829-1.5937-0.91487-1.6738-1.5528-2.2812-2.9394-0.60741-1.3867-2.033-3.4622-4.8848-4.2188-0.71295-0.18913-1.389-0.26246-2.0215-0.25195z" style="paint-order:markers fill stroke"/>
+ <g id="text1242" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1372" d="m70.306 150.91q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21054 2.1639 0.21054 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28072-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15207-2.1639-0.15207-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.08773 0.15791-0.12867 0.32167-0.03509 0.16376-0.03509 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61994 0.30412 0.38016 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247t-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1374" d="m74.16 142.58h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1376" d="m85.009 142.58v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1378" d="m85.752 142.58h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67843 0.29242 0.25733 0.70767 0.25733t0.70182-0.25733q0.29243-0.25734 0.38015-0.67843 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52051-1.6376 0.52051-0.95331 0-1.6434-0.52051-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1380" d="m93.607 143.98v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75445 0-0.44449-0.23979-0.75446-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87142 0.52637 0.66673 0.52637 1.6376 0 0.97085-0.52637 1.6434-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1246" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1361" d="m102.96 142.58v1.4036h-1.8715v6.8311h-1.4972v-6.8311h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1363" d="m105.19 145.19q-0.0877 0.47958-0.0877 1.5089t0.0877 1.5089q0.10528 0.56731 0.38601 0.93576 0.28657 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36845 0.39185-0.93576 0.0877-0.48542 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45619 0-0.74276 0.36846-0.28073 0.36261-0.38601 0.92991zm-1.4212 3.55q-0.16376-0.64919-0.16376-2.0411 0-1.392 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64919-0.6141 1.6668-0.6141t1.6668 0.6141q0.64919 0.60824 0.88313 1.5557 0.16375 0.64918 0.16375 2.0411 0 1.3919-0.16375 2.0411-0.23394 0.94746-0.88313 1.5616-0.64918 0.60824-1.6668 0.60824t-1.6668-0.60824q-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1365" d="m111.18 145.19q-0.0877 0.47958-0.0877 1.5089t0.0877 1.5089q0.10527 0.56731 0.386 0.93576 0.28658 0.36261 0.74276 0.36261t0.73691-0.36261q0.28658-0.36845 0.39185-0.93576 0.0877-0.48542 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.5673-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846t-0.74276 0.36846q-0.28073 0.36261-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64919-0.16376-2.0411 0-1.392 0.16376-2.0411 0.23394-0.94746 0.88313-1.5557 0.64918-0.6141 1.6668-0.6141t1.6668 0.6141q0.64919 0.60824 0.88313 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.3919-0.16376 2.0411-0.23394 0.94746-0.88313 1.5616-0.64918 0.60824-1.6668 0.60824t-1.6668-0.60824q-0.64919-0.61409-0.88313-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1367" d="m116.04 150.81v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1369" d="m124.15 150.91q-0.95916 0-2.164-0.18715v-1.427q1.4095 0.21054 2.164 0.21054 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28072-0.41524-0.386-0.16961-0.0643-0.46789-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69013 1.661-0.69013 0.78954 0 2.1639 0.14621v1.4095q-1.4212-0.15207-2.1639-0.15207-0.22225 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.0877 0.15791-0.12867 0.32167-0.0351 0.16376-0.0351 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69013 0.15791 0.54391 0.0819 0.87727 0.25733 0.46788 0.23979 0.77201 0.61994 0.30412 0.38016 0.42109 0.76616t0.11697 0.80124q0 1.1346-0.70182 1.8247t-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <path id="rect1252" d="m45.345 167.28h82.571c0.28796 0 0.51978 0.23182 0.51978 0.51978v22.171c0 0.28796-0.23182 0.51978-0.51978 0.51978h-82.571c-0.28796 0-0.51978-0.23182-0.51978-0.51978v-22.171c0-0.28796 0.23182-0.51978 0.51978-0.51978z" style="paint-order:markers fill stroke"/>
+ <path id="rect1254" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m645.59 394.06c-2.1498-0.02041-6.2168 0.06055-6.2168 0.06055-0.19725 0-0.35547 0.16744-0.35547 0.37304v2.5234h-3.7051v-2.5234c0-0.2056-0.15821-0.37304-0.35547-0.37304h-4.416c-0.19725 0-0.35742 0.16744-0.35742 0.37304v9.3535c0 0.20561 0.16001 0.37109 0.35742 0.37109h4.416c0.19726 0 0.35547-0.16548 0.35547-0.37109v-2.5234h3.7051v2.5234c0 0.20561 0.15825 0.37109 0.35547 0.37109h2.2363v11.904c-1.2817 0.50867-2.1856 1.7516-2.1856 3.2188v18.037c0 1.9216 1.5471 3.4688 3.4688 3.4688s3.4688-1.5471 3.4688-3.4688v-18.037c0-1.4665-0.90278-2.7096-2.1836-3.2188v-11.904h2.4199c0.38561-0.3394 0.75677-1.7031 2.8164-2.7441 5.5309-1.9 7.9328 2.808 7.8906 2.1582-0.04505-0.69374-0.35053-5.0458-5.1797-7.5977-2.1039-0.94609-4.5788-1.8754-5.2461-1.9141-0.06886-0.03629-0.57246-0.05375-1.2891-0.06055z" fill="#fff" stroke="#fff" stroke-width=".82412" style="paint-order:markers fill stroke"/>
+ <path id="path1262" transform="matrix(.26458 0 0 .26458 -113.76 68.446)" d="m650.43 379.6c-1.8975 0.03152-3.3994 0.81663-4.3105 1.4941-1.2149 0.90323-1.6057 1.4212-3.4434 1.4258-1.8376 0.00378-2.2279-0.51301-3.4473-1.4102-1.2194-0.89718-3.4899-1.9778-6.3379-1.207-2.848 0.77068-4.2647 2.8506-4.8652 4.2402-0.60051 1.3896-0.67842 2.0323-2.2676 2.9551-1.5892 0.92281-2.1868 0.67314-3.6914 0.50586-1.5046-0.16743-4.0146 0.03049-6.0957 2.1211-2.0811 2.0914-2.2685 4.6037-2.0938 6.1074 0.17473 1.5038 0.42851 2.0996-0.48633 3.6934-0.91487 1.5937-1.5548 1.6738-2.9414 2.2812-1.3867 0.60741-3.4622 2.031-4.2188 4.8828-0.75654 2.8518 0.34098 5.1191 1.2441 6.334 0.90319 1.2149 1.4192 1.6038 1.4238 3.4414 0.00454 1.8376-0.51104 2.2279-1.4082 3.4473-0.89718 1.2194-1.9797 3.4918-1.209 6.3398 0.77071 2.848 2.8506 4.2628 4.2402 4.8633 1.3896 0.60049 2.0323 0.68039 2.9551 2.2695 0.92276 1.5892 0.67316 2.1849 0.50586 3.6895-0.16728 1.5046 0.0297 4.0166 2.1211 6.0976 2.0915 2.081 4.6037 2.2627 6.1074 2.0879 1.5037-0.17461 2.0996-0.42268 3.6934 0.49219 1.5937 0.91483 1.6738 1.5528 2.2812 2.9394 0.60742 1.3866 2.031 3.4622 4.8828 4.2188 2.8518 0.75655 6.3494-0.68758 6.334-1.2422-0.01587-0.55461-2.3574 0.39899-4.7051-1.0781-2.3477-1.4772-2.1797-2.3888-2.6973-3.5703-0.51759-1.1816-1.4424-3.4001-4.0254-4.8828-2.5829-1.4827-4.9648-1.1626-6.2461-1.0137-1.2813 0.14892-1.3671 0.40956-2.6894-0.90625-1.3224-1.3158-1.0586-1.3996-0.91602-2.6816 0.14257-1.282 0.44867-3.6628-1.0469-6.2383-1.4956-2.5756-3.7163-3.4922-4.9004-4.0039-1.1841-0.51171-1.3857-0.32643-1.873-2.127-0.4873-1.8008-0.22347-1.7481 0.54102-2.7871 0.76447-1.039 2.2242-2.9456 2.2168-5.9238-0.00718-2.9783-1.4764-4.8828-2.2461-5.918-0.76963-1.0352-1.037-0.97812-0.55859-2.7812 0.47833-1.8031 0.68366-1.6172 1.8652-2.1348 1.1816-0.51757 3.4001-1.4444 4.8828-4.0273 1.4827-2.583 1.1626-4.9628 1.0137-6.2441-0.14883-1.2813-0.4076-1.3671 0.90821-2.6895s1.3976-1.0644 2.6797-0.92187c1.282 0.14249 3.6647 0.45454 6.2402-1.041 2.5755-1.4955 3.4864-3.7163 3.998-4.9004 0.5117-1.1842 0.33013-1.3877 2.1309-1.875 1.8007-0.48733 1.7481-0.22151 2.7871 0.54297 1.039 0.76449 2.9456 2.2242 5.9238 2.2168 2.9783-0.00756 4.8789-1.4765 5.9141-2.2461 1.0352-0.76966 0.97813-1.0369 2.7812-0.55859 1.8031 0.47834 1.6211 0.68368 2.1387 1.8652 0.51758 1.1816 1.4444 3.4001 4.0273 4.8828s4.9648 1.1626 6.2461 1.0137c1.2813-0.14891 1.3651-0.40956 2.6875 0.90625 1.3224 1.3158 1.0605 1.3996 0.91796 2.6816-0.14252 1.282-0.45062 3.6627 1.0449 6.2383 1.4955 2.5755 3.7162 3.4883 4.9004 4 1.1841 0.51171 1.3877 0.33014 1.875 2.1309 0.4873 1.8007 0.22346 1.7481-0.54102 2.7871-0.76448 1.039-2.2262 2.9456-2.2188 5.9238 0.00756 2.9783 1.4765 4.8769 2.2461 5.9121 0.76965 1.0352 1.0369 0.98006 0.5586 2.7832-0.47834 1.8031-0.68366 1.6211-1.8652 2.1387-1.1816 0.51757-3.4001 1.4444-4.8828 4.0274-1.4827 2.583-1.1606 4.9628-1.0117 6.2441 0.14892 1.2813 0.40761 1.3671-0.9082 2.6894s-1.3996 1.0605-2.6816 0.91797c-1.2821-0.14249-3.6627-0.4506-6.2383 1.0449-2.5756 1.4956-3.4922 3.7163-4.0039 4.9004-0.42128 0.97492-0.43101 1.2897-1.4043 1.6406-0.13992-0.07663-0.29747-0.15087-0.48828-0.22266-1.6607-0.62483-1.8791 0.21908-3.2422 1.2344-1.2233 0.91121-2.0946 1.027-2.0469 2.2246-0.11719-0.01206-0.22826-0.01415-0.3164-0.0039-0.27829 0.03401-0.38865 0.07948-0.6836-0.08985-0.29495-0.16932-0.30944-0.28829-0.42187-0.54492-0.11241-0.25663-0.3746-0.64125-0.90235-0.78125-0.52779-0.13984-1.1767 0.12767-1.1738 0.23047 0.00294 0.10243 0.43663-0.07442 0.8711 0.19922 0.43447 0.27326 0.40421 0.44343 0.5 0.66211 0.09573 0.21883 0.26613 0.62791 0.74414 0.90234 0.47801 0.2744 0.91911 0.21509 1.1562 0.1875 0.17999-0.02008 0.25507-0.01352 0.37695 0.07813 0.37226 1.1966 1.2449 1.0814 2.752 1.6484 1.6608 0.62483 2.1701 1.3766 3.5332 0.36133 0.99774-0.74318 0.89279-1.2827 0.65234-2.1582 2.2207-0.92783 3.3884-2.6886 3.918-3.9141 0.60051-1.3897 0.67842-2.0323 2.2676-2.9551s2.1868-0.67314 3.6914-0.50586c1.5046 0.16743 4.0146-0.03045 6.0957-2.1211 2.0811-2.0914 2.2685-4.6037 2.0938-6.1074-0.17477-1.5037-0.42853-2.0977 0.48633-3.6914 0.91486-1.5938 1.5548-1.6758 2.9414-2.2832 1.3866-0.6074 3.4622-2.031 4.2188-4.8828 0.75654-2.8518-0.34091-5.1191-1.2441-6.334-0.90322-1.2149-1.4193-1.6038-1.4238-3.4414-0.00453-1.8376 0.51104-2.2279 1.4082-3.4473 0.89716-1.2194 1.9797-3.4919 1.209-6.3398-0.77071-2.848-2.8506-4.2628-4.2402-4.8633-1.3896-0.60053-2.0323-0.6784-2.9551-2.2676-0.92278-1.5891-0.67315-2.1868-0.50586-3.6914 0.16728-1.5046-0.02971-4.0147-2.1211-6.0957-2.0914-2.0811-4.6037-2.2685-6.1074-2.0938-1.5037 0.17462-2.0996 0.42659-3.6934-0.48828-1.5937-0.91487-1.6738-1.5528-2.2812-2.9394-0.60741-1.3867-2.033-3.4622-4.8848-4.2188-0.71295-0.18913-1.389-0.26246-2.0215-0.25195z" fill="#fff" style="paint-order:markers fill stroke"/>
+ <g id="text1274" fill="#fff" stroke-width=".22458" aria-label="SETUP">
+ <path id="path1333" d="m70.306 183.09q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.08773 0.15791-0.12867 0.32167-0.03509 0.16376-0.03509 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25733 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1335" d="m74.16 174.76h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1337" d="m85.009 174.76v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1339" d="m85.752 174.76h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29243-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82464-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052-0.95331 0-1.6434-0.52052-0.68428-0.52051-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1341" d="m93.607 176.17v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446 0-0.44448-0.23979-0.75445-0.23394-0.30998-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52637 0.66673 0.52637 1.6376 0 0.97086-0.52637 1.6434-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1278" fill="#fff" stroke-width=".22458" aria-label="TOOLS">
+ <path id="path1344" d="m102.96 174.76v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1346" d="m105.19 177.37q-0.0877 0.47958-0.0877 1.5089 0 1.0293 0.0877 1.5089 0.10528 0.56731 0.38601 0.93576 0.28657 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36845 0.39185-0.93576 0.0877-0.48543 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45619 0-0.74276 0.36846-0.28073 0.3626-0.38601 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88312-1.5557 0.64919-0.61409 1.6668-0.61409t1.6668 0.61409q0.64919 0.60825 0.88313 1.5557 0.16375 0.64918 0.16375 2.0411 0 1.392-0.16375 2.0411-0.23394 0.94746-0.88313 1.5616-0.64918 0.60824-1.6668 0.60824t-1.6668-0.60824q-0.64918-0.6141-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1348" d="m111.18 177.37q-0.0877 0.47958-0.0877 1.5089 0 1.0293 0.0877 1.5089 0.10527 0.56731 0.386 0.93576 0.28658 0.36261 0.74276 0.36261t0.73691-0.36261q0.28658-0.36845 0.39185-0.93576 0.0877-0.48543 0.0877-1.5089 0-1.0293-0.0877-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846t-0.74276 0.36846q-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94746 0.88313-1.5557 0.64918-0.61409 1.6668-0.61409t1.6668 0.61409q0.64919 0.60825 0.88313 1.5557 0.16376 0.64918 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88313 1.5616-0.64918 0.60824-1.6668 0.60824t-1.6668-0.60824q-0.64919-0.6141-0.88313-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1350" d="m116.04 183v-8.2347h1.4972v6.831h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1352" d="m124.15 183.09q-0.95916 0-2.164-0.18715v-1.427q1.4095 0.21055 2.164 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46789-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78954 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22225 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25733-0.0877 0.15791-0.12867 0.32167-0.0351 0.16376-0.0351 0.32167 0 0.69012 0.46788 0.90067 0.15206 0.0702 0.69013 0.15791 0.54391 0.0819 0.87727 0.25733 0.46788 0.23979 0.77201 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
+</svg>
diff --git a/docs/images/logo-editable-inkscape.png b/docs/images/logo-editable-inkscape.png
deleted file mode 100644
index 09036fcc..00000000
--- a/docs/images/logo-editable-inkscape.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-inline-negative.png b/docs/images/logo-inline-negative.png
deleted file mode 100644
index 6718373f..00000000
--- a/docs/images/logo-inline-negative.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-inline-negative.svg b/docs/images/logo-inline-negative.svg
index deed96e6..4bf63cfe 100644
--- a/docs/images/logo-inline-negative.svg
+++ b/docs/images/logo-inline-negative.svg
@@ -1,105 +1,35 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- width="83.810921mm"
- height="23.41044mm"
- viewBox="0 0 83.810921 23.41044"
- version="1.1"
- id="svg1021">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <rect
- ry="0.51978147"
- y="0.1"
- x="0.1"
- height="23.21044"
- width="83.610924"
- id="rect830"
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.48264033;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- <g
- id="layer1"
- transform="translate(-2.6008009,-95.497324)">
- <g
- id="g1751"
- transform="matrix(0.45669594,0,0,0.45669594,-11.041899,60.847347)">
- <path
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- id="rect934" />
- <path
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- id="path944" />
- </g>
- <g
- id="g1747"
- transform="translate(0,-23.386941)">
- <g
- id="text1023"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="SETUP">
- <path
- id="path1713"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- <path
- id="path1715"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z" />
- <path
- id="path1717"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1719"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z" />
- <path
- id="path1721"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z" />
- </g>
- <g
- id="text1661"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="TOOLS">
- <path
- id="path1724"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1726"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1728"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1730"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z" />
- <path
- id="path1732"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="83.811mm" height="23.41mm" version="1.1" viewBox="0 0 83.811 23.41" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect id="rect830" x=".1" y=".1" width="83.611" height="23.21" ry=".51978" fill="#336790" style="paint-order:markers fill stroke"/>
+ <g id="layer1" transform="translate(-2.6008 -95.497)">
+ <g id="g1751" transform="matrix(.4567 0 0 .4567 -11.042 60.847)">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#fff" style="paint-order:markers fill stroke"/>
</g>
+ <g id="g1747" transform="translate(0 -23.387)" stroke-width=".22458">
+ <g id="text1023" fill="#fff" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
+ </g>
</svg>
diff --git a/docs/images/logo-inline.png b/docs/images/logo-inline.png
deleted file mode 100644
index 33e0e484..00000000
--- a/docs/images/logo-inline.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-inline.svg b/docs/images/logo-inline.svg
index 11ab7df7..6e45103d 100644
--- a/docs/images/logo-inline.svg
+++ b/docs/images/logo-inline.svg
@@ -1,97 +1,34 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- width="80.121315mm"
- height="20mm"
- viewBox="0 0 80.121315 20"
- version="1.1"
- id="svg1021">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1"
- transform="translate(-4.4456067,-97.202544)">
- <g
- id="g1751"
- transform="matrix(0.45669594,0,0,0.45669594,-11.041899,60.847347)">
- <path
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- id="rect934" />
- <path
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- id="path944" />
- </g>
- <g
- id="g1747"
- transform="translate(0,-23.386941)">
- <g
- id="text1023"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#336790;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="SETUP">
- <path
- id="path1713"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- <path
- id="path1715"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z" />
- <path
- id="path1717"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1719"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z" />
- <path
- id="path1721"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z" />
- </g>
- <g
- id="text1661"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="TOOLS">
- <path
- id="path1724"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1726"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1728"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1730"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z" />
- <path
- id="path1732"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="80.121mm" height="20mm" version="1.1" viewBox="0 0 80.121 20" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(-4.4456 -97.203)">
+ <g id="g1751" transform="matrix(.4567 0 0 .4567 -11.042 60.847)">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#336790" style="paint-order:markers fill stroke"/>
</g>
+ <g id="g1747" transform="translate(0 -23.387)" stroke-width=".22458">
+ <g id="text1023" fill="#336790" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
+ </g>
</svg>
diff --git a/docs/images/logo-negative.png b/docs/images/logo-negative.png
deleted file mode 100644
index c025f4fd..00000000
--- a/docs/images/logo-negative.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-negative.svg b/docs/images/logo-negative.svg
index 23a553d3..d2142045 100644
--- a/docs/images/logo-negative.svg
+++ b/docs/images/logo-negative.svg
@@ -1,106 +1,37 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- width="64.584335mm"
- height="60.751602mm"
- viewBox="0 0 64.584336 60.751602"
- version="1.1"
- id="svg1021">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <rect
- ry="1.57368"
- y="0.1"
- x="0.1"
- height="60.551601"
- width="64.384338"
- id="rect830"
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.75281364;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- <g
- id="layer1"
- transform="translate(-22.929862,-76.826813)">
- <g
- id="g1769">
- <g
- id="g1751">
- <path
- id="rect934"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- <path
- id="path944"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- </g>
- <g
- id="g1747">
- <g
- aria-label="SETUP"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- id="text1023">
- <path
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- id="path1713" />
- <path
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- id="path1715" />
- <path
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- id="path1717" />
- <path
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- id="path1719" />
- <path
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke-width:0.22458273"
- id="path1721" />
- </g>
- <g
- aria-label="TOOLS"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- id="text1661">
- <path
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1724" />
- <path
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1726" />
- <path
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1728" />
- <path
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1730" />
- <path
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1732" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="64.584mm" height="60.752mm" version="1.1" viewBox="0 0 64.584 60.752" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect id="rect830" x=".1" y=".1" width="64.384" height="60.552" ry="1.5737" fill="#336790" style="paint-order:markers fill stroke"/>
+ <g id="layer1" transform="translate(-22.93 -76.827)">
+ <g id="g1769">
+ <g id="g1751">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#fff" style="paint-order:markers fill stroke"/>
+ </g>
+ <g id="g1747" stroke-width=".22458">
+ <g id="text1023" fill="#fff" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
</g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
</g>
+ </g>
</svg>
diff --git a/docs/images/logo-over-white.png b/docs/images/logo-over-white.png
deleted file mode 100644
index 60bbff30..00000000
--- a/docs/images/logo-over-white.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-over-white.svg b/docs/images/logo-over-white.svg
index 3ae3968e..1ed01380 100644
--- a/docs/images/logo-over-white.svg
+++ b/docs/images/logo-over-white.svg
@@ -1,106 +1,37 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- width="64.584335mm"
- height="60.751602mm"
- viewBox="0 0 64.584335 60.751602"
- version="1.1"
- id="svg1021">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <rect
- style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.75281364;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- id="rect1616"
- width="64.384338"
- height="60.551601"
- x="0.1"
- y="0.1"
- ry="1.57368" />
- <g
- id="layer1"
- transform="translate(-22.929862,-76.826813)">
- <g
- id="g1769">
- <g
- id="g1751">
- <path
- id="rect934"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- <path
- id="path944"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- </g>
- <g
- id="g1747">
- <g
- aria-label="SETUP"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#336790;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- id="text1023">
- <path
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1713" />
- <path
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1715" />
- <path
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1717" />
- <path
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1719" />
- <path
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1721" />
- </g>
- <g
- aria-label="TOOLS"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- id="text1661">
- <path
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1724" />
- <path
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1726" />
- <path
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1728" />
- <path
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1730" />
- <path
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1732" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="64.584mm" height="60.752mm" version="1.1" viewBox="0 0 64.584 60.752" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <rect id="rect1616" x=".1" y=".1" width="64.384" height="60.552" ry="1.5737" fill="#fff" style="paint-order:markers fill stroke"/>
+ <g id="layer1" transform="translate(-22.93 -76.827)">
+ <g id="g1769">
+ <g id="g1751">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#336790" style="paint-order:markers fill stroke"/>
+ </g>
+ <g id="g1747" stroke-width=".22458">
+ <g id="text1023" fill="#336790" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
</g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
</g>
+ </g>
</svg>
diff --git a/docs/images/logo-symbol-only.png b/docs/images/logo-symbol-only.png
deleted file mode 100644
index 9cf13f8f..00000000
--- a/docs/images/logo-symbol-only.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-symbol-only.svg b/docs/images/logo-symbol-only.svg
index febdb3e4..2bbf2d58 100644
--- a/docs/images/logo-symbol-only.svg
+++ b/docs/images/logo-symbol-only.svg
@@ -1,46 +1,20 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- width="43.419624mm"
- height="44.592812mm"
- viewBox="0 0 43.419624 44.592811"
- version="1.1"
- id="svg1021">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1"
- transform="translate(-33.51222,-84.906137)">
- <g
- id="g1769">
- <g
- transform="translate(1.4742777e-4,5.70132)"
- id="g1751">
- <path
- id="rect934"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- <path
- id="path944"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="192" height="192" version="1.1" viewBox="0 0 50.8 50.8" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="matrix(1.1479 0 0 1.1479 -37.991 -97.66)">
+ <g id="g1769">
+ <g id="g1751" transform="translate(.00014743 5.7013)">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#336790" style="paint-order:markers fill stroke"/>
+ </g>
</g>
+ </g>
</svg>
diff --git a/docs/images/logo-text-only.png b/docs/images/logo-text-only.png
deleted file mode 100644
index 0c5500ba..00000000
--- a/docs/images/logo-text-only.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo-text-only.svg b/docs/images/logo-text-only.svg
index a59731d4..2e92580d 100644
--- a/docs/images/logo-text-only.svg
+++ b/docs/images/logo-text-only.svg
@@ -1,85 +1,30 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- id="svg1021"
- version="1.1"
- viewBox="0 0 59.489777 9.2218504"
- height="9.2218504mm"
- width="59.489777mm">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(-25.477144,-102.59162)"
- id="layer1">
- <g
- transform="translate(0,-23.386941)"
- id="g1747">
- <g
- aria-label="SETUP"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#336790;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- id="text1023">
- <path
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1713" />
- <path
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1715" />
- <path
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1717" />
- <path
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1719" />
- <path
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- id="path1721" />
- </g>
- <g
- aria-label="TOOLS"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- id="text1661">
- <path
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1724" />
- <path
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1726" />
- <path
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1728" />
- <path
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1730" />
- <path
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- id="path1732" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="59.49mm" height="9.2219mm" version="1.1" viewBox="0 0 59.49 9.2219" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(-25.477 -102.59)">
+ <g id="g1747" transform="translate(0 -23.387)" stroke-width=".22458">
+ <g id="text1023" fill="#336790" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
</g>
+ </g>
</svg>
diff --git a/docs/images/logo.png b/docs/images/logo.png
deleted file mode 100644
index 3f821254..00000000
--- a/docs/images/logo.png
+++ /dev/null
Binary files differ
diff --git a/docs/images/logo.svg b/docs/images/logo.svg
index 103d294f..7c793a08 100644
--- a/docs/images/logo.svg
+++ b/docs/images/logo.svg
@@ -1,98 +1,36 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- id="svg1021"
- version="1.1"
- viewBox="0 0 60.536667 56.932983"
- height="56.932983mm"
- width="60.536667mm">
- <defs
- id="defs1015" />
- <metadata
- id="metadata1018">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- transform="translate(-24.953698,-78.736052)"
- id="layer1">
- <g
- id="g1769">
- <g
- id="g1751">
- <path
- style="fill:#e5b62f;fill-opacity:1;fill-rule:nonzero;stroke:#e5b62f;stroke-width:0.4774465;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 56.863889,87.985612 c -1.245468,-0.01183 -3.601847,0.03564 -3.601847,0.03564 -0.114287,0 -0.20619,0.09589 -0.20619,0.214976 v 1.46244 H 50.90973 v -1.46244 c 0,-0.1191 -0.0919,-0.214976 -0.206189,-0.214976 h -2.559017 c -0.114286,0 -0.206705,0.09589 -0.206705,0.214976 v 5.418788 c 0,0.1191 0.09235,0.214977 0.206705,0.214977 h 2.559017 c 0.114287,0 0.206189,-0.09587 0.206189,-0.214977 v -1.461923 h 2.146122 v 1.461923 c 0,0.1191 0.09191,0.214977 0.20619,0.214977 h 1.295011 v 6.895187 c -0.742529,0.29471 -1.265555,1.01661 -1.265555,1.86655 v 10.45001 c 0,1.11329 0.895885,2.00918 2.00918,2.00918 1.113292,0 2.009695,-0.89589 2.009695,-2.00918 v -10.45001 c 0,-0.84994 -0.523022,-1.57184 -1.265555,-1.86655 v -6.895187 h 1.402499 c 0.223398,-0.196684 0.438705,-0.985378 1.631942,-1.588534 3.204289,-1.100736 4.595703,1.625992 4.571295,1.249532 -0.02611,-0.401913 -0.203636,-2.922341 -3.001367,-4.400762 -1.218895,-0.548108 -2.652008,-1.086577 -3.038575,-1.108977 -0.03989,-0.02102 -0.331568,-0.03172 -0.746723,-0.03564 z"
- id="rect934" />
- <path
- style="fill:#336790;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.19989915;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;paint-order:markers fill stroke"
- d="m 59.665281,79.605246 c -1.099312,0.01828 -1.969648,0.473642 -2.497521,0.866095 -0.703832,0.523277 -0.929574,0.822626 -1.994194,0.825277 -1.06462,0.0027 -1.290873,-0.296205 -1.997295,-0.815972 -0.706421,-0.519766 -2.022679,-1.14672 -3.672644,-0.700215 -1.649965,0.446506 -2.470018,1.651613 -2.817916,2.456698 -0.347897,0.80508 -0.393465,1.177432 -1.314132,1.712036 -0.920666,0.534609 -1.266695,0.389926 -2.13837,0.293006 -0.871661,-0.09692 -2.325918,0.0172 -3.531568,1.228868 -1.205665,1.211665 -1.314089,2.667116 -1.212846,3.538286 0.101228,0.871175 0.247854,1.216089 -0.282153,2.139404 -0.530022,0.923314 -0.900427,0.969986 -1.703772,1.321884 -0.803345,0.351898 -2.005999,1.177126 -2.444296,2.829281 -0.438296,1.652161 0.197124,2.965197 0.72037,3.66903 0.523262,0.703833 0.822625,0.929572 0.825273,1.994196 0.0026,1.06462 -0.296213,1.29035 -0.815971,1.99677 -0.519773,0.70643 -1.146203,2.02269 -0.699699,3.67265 0.446504,1.64996 1.651089,2.47053 2.456181,2.81843 0.805077,0.3479 1.17743,0.39347 1.71204,1.31414 0.534596,0.92066 0.389929,1.26618 0.293006,2.13785 -0.09692,0.87166 0.01712,2.32643 1.228865,3.53208 1.211669,1.20565 2.667112,1.311 3.538286,1.20975 0.871174,-0.10126 1.21609,-0.24477 2.139405,0.28525 0.923314,0.53002 0.96998,0.90043 1.321882,1.70377 0.351903,0.80334 1.177131,2.00548 2.829285,2.44378 1.652155,0.43829 3.67799,-0.39906 3.669028,-0.72037 -0.0091,-0.3213 -1.365829,0.23152 -2.725932,-0.62425 -1.360103,-0.85577 -1.262319,-1.38407 -1.562179,-2.06861 -0.299861,-0.68453 -0.836256,-1.96976 -2.332674,-2.82876 -1.496404,-0.859 -2.876052,-0.67281 -3.618384,-0.58653 -0.742333,0.0863 -0.791945,0.23675 -1.558045,-0.52555 -0.7661,-0.76231 -0.613297,-0.81116 -0.530717,-1.55391 0.08259,-0.74275 0.260268,-2.12213 -0.606165,-3.61425 -0.866433,-1.49213 -2.153087,-2.02279 -2.839103,-2.31924 -0.686016,-0.29645 -0.803407,-0.18822 -1.085722,-1.23145 -0.282315,-1.04323 -0.129234,-1.01345 0.313676,-1.61541 0.442894,-0.60195 1.288974,-1.7064 1.284676,-3.43183 -0.0042,-1.725427 -0.855847,-2.828991 -1.30173,-3.428727 -0.445882,-0.599742 -0.600626,-0.566654 -0.323494,-1.611273 0.277117,-1.044619 0.396537,-0.936759 1.081071,-1.23662 0.684535,-0.299854 1.969763,-0.836259 2.828768,-2.332673 0.85899,-1.496409 0.672807,-2.876051 0.586528,-3.618385 -0.08624,-0.742328 -0.23624,-0.791936 0.526065,-1.558041 0.762306,-0.766106 0.810639,-0.616402 1.553394,-0.533819 0.74274,0.08258 2.122129,0.263369 3.61425,-0.603064 1.492121,-0.866434 2.020204,-2.153086 2.316655,-2.839103 0.29645,-0.686018 0.191317,-0.803408 1.234549,-1.085722 1.043233,-0.282314 1.012934,-0.129212 1.614888,0.313676 0.601956,0.442893 1.706409,1.288969 3.431833,1.284673 1.72543,-0.0043 2.825891,-0.855327 3.42563,-1.301209 0.599739,-0.445887 0.567168,-0.601134 1.611789,-0.324013 1.044619,0.27712 0.939345,0.396538 1.2392,1.081071 0.299857,0.684534 0.836779,1.969772 2.333191,2.828771 1.496414,0.858999 2.875536,0.67332 3.617867,0.587044 0.742334,-0.08628 0.791938,-0.236759 1.558044,0.525546 0.766107,0.762305 0.613818,0.810644 0.531236,1.553397 -0.08258,0.742748 -0.260784,2.122126 0.605648,3.614245 0.86643,1.492124 2.153086,2.020213 2.839103,2.316659 0.686017,0.296447 0.803923,0.191318 1.086239,1.23455 0.282313,1.043226 0.128696,1.012939 -0.314193,1.614885 -0.442896,0.601952 -1.288968,1.706403 -1.284676,3.431832 0.0043,1.72543 0.855845,2.82589 1.301729,3.42563 0.445883,0.59974 0.600615,0.56717 0.323496,1.61179 -0.277125,1.04462 -0.396539,0.93986 -1.081072,1.23972 -0.684536,0.29985 -1.96977,0.83626 -2.828769,2.33267 -0.858996,1.49642 -0.672804,2.87554 -0.586528,3.61787 0.08628,0.74233 0.236756,0.79194 -0.525548,1.55804 -0.762307,0.76611 -0.811159,0.61382 -1.553911,0.53124 -0.742751,-0.0826 -2.122128,-0.26027 -3.614251,0.60616 -1.492118,0.86644 -2.023308,2.15309 -2.319753,2.8391 -0.246329,0.57005 -0.222236,0.74776 -0.802536,0.95343 -0.08286,-0.046 -0.17925,-0.0898 -0.293521,-0.1328 -0.962139,-0.36199 -1.088237,0.12751 -1.877923,0.71571 -0.710128,0.52895 -1.218404,0.5911 -1.188558,1.28882 -0.06706,-0.007 -0.130334,-0.008 -0.180867,-0.002 -0.161229,0.0187 -0.224964,0.0454 -0.395841,-0.0527 -0.170875,-0.0981 -0.179305,-0.16656 -0.244429,-0.31522 -0.06513,-0.14868 -0.217729,-0.37158 -0.523484,-0.45269 -0.305771,-0.0811 -0.680696,0.0739 -0.679026,0.13332 0.0016,0.0594 0.252655,-0.0426 0.504361,0.11576 0.251707,0.15837 0.233379,0.25624 0.28887,0.38292 0.05548,0.1267 0.155085,0.36451 0.432017,0.52349 0.276934,0.15896 0.531828,0.12449 0.669208,0.10851 0.104899,-0.0122 0.136234,-0.0302 0.20774,0.0243 0.210754,0.72266 0.721156,0.6429 1.605587,0.97565 0.962139,0.36199 1.256184,0.7975 2.045869,0.20929 0.578536,-0.43092 0.52042,-0.74207 0.380855,-1.25005 1.285693,-0.53767 1.960354,-1.55784 2.267045,-2.26756 0.347901,-0.80508 0.393468,-1.17692 1.314133,-1.71153 0.920665,-0.5346 1.26619,-0.39043 2.137855,-0.29351 0.871664,0.0969 2.326431,-0.0167 3.532084,-1.22836 1.205653,-1.21166 1.313579,-2.66762 1.212329,-3.5388 -0.101259,-0.87117 -0.247349,-1.21557 0.28267,-2.13889 0.530016,-0.92331 0.90043,-0.9705 1.703771,-1.3224 0.803344,-0.35189 2.005486,-1.17661 2.443779,-2.82876 0.438293,-1.65216 -0.197101,-2.9652 -0.72037,-3.66903 -0.523275,-0.70383 -0.822109,-0.92957 -0.824755,-1.9942 -0.0026,-1.064617 0.296204,-1.290862 0.81597,-1.997291 0.519766,-0.706424 1.146207,-2.022685 0.699698,-3.672642 C 75.90923,93.54616 74.704122,92.726098 73.899037,92.3782 73.093955,92.030302 72.72212,91.984737 72.187513,91.064067 c -0.534601,-0.920664 -0.390437,-1.266702 -0.293521,-2.138366 0.09692,-0.87167 -0.0167,-2.325916 -1.228349,-3.531571 -1.211665,-1.20565 -2.667629,-1.314095 -3.538802,-1.212843 -0.87117,0.101258 -1.215573,0.247861 -2.138887,-0.282152 -0.923314,-0.530018 -0.970502,-0.900435 -1.3224,-1.703774 -0.351899,-0.803295 -1.176611,-2.00595 -2.828769,-2.444247 -0.413039,-0.109574 -0.805067,-0.151817 -1.171504,-0.145726 z"
- id="path944" />
- </g>
- <g
- id="g1747">
- <g
- id="text1023"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#336790;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="SETUP">
- <path
- id="path1713"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 28.181456,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543912,0.0819 0.877276,0.25734 0.467881,0.23979 0.772004,0.61994 0.304122,0.38015 0.421092,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- <path
- id="path1715"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 32.035622,126.47213 h 4.491654 v 1.40365 H 33.53284 v 1.98849 h 2.245827 v 1.40364 H 33.53284 v 2.03528 h 2.994436 v 1.40364 h -4.491654 z" />
- <path
- id="path1717"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 42.884604,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1719"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 43.627365,126.47213 h 1.497218 v 5.24027 q 0,0.54976 0.04094,0.74861 0.08773,0.42109 0.374305,0.67842 0.292425,0.25734 0.707669,0.25734 0.415244,0 0.701821,-0.25734 0.292425,-0.25733 0.380153,-0.67842 0.04094,-0.19885 0.04094,-0.74861 v -5.24027 h 1.497218 v 5.24027 q 0,0.82464 -0.09358,1.22234 -0.198849,0.82463 -0.888973,1.34515 -0.684275,0.52052 -1.637582,0.52052 -0.953307,0 -1.643431,-0.52052 -0.684275,-0.52052 -0.883124,-1.34515 -0.09358,-0.3977 -0.09358,-1.22234 z" />
- <path
- id="path1721"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#336790;fill-opacity:1;stroke-width:0.22458273"
- d="m 51.48191,127.87578 v 2.38619 h 0.362607 q 0.502972,0 0.731064,-0.0468 0.397698,-0.0819 0.631638,-0.39185 0.239789,-0.30997 0.239789,-0.75446 0,-0.44449 -0.239789,-0.75446 -0.23394,-0.30997 -0.631638,-0.39185 -0.228092,-0.0468 -0.731064,-0.0468 z m -1.497218,-1.40365 h 1.848128 q 0.865579,0 1.234035,0.0877 0.830488,0.19885 1.351006,0.87143 0.526365,0.66673 0.526365,1.63758 0,0.97085 -0.526365,1.64343 -0.520518,0.66673 -1.351006,0.86558 -0.368456,0.0877 -1.234035,0.0877 h -0.35091 v 3.04122 h -1.497218 z" />
- </g>
- <g
- id="text1661"
- style="font-style:normal;font-weight:normal;font-size:8.98330784px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#e5b62f;fill-opacity:1;stroke:none;stroke-width:0.22458273"
- aria-label="TOOLS">
- <path
- id="path1724"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 60.833675,126.47213 v 1.40365 h -1.871523 v 6.83105 h -1.497218 v -6.83105 h -1.871522 v -1.40365 z" />
- <path
- id="path1726"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 63.067804,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105274,0.5673 0.386002,0.93576 0.286577,0.36261 0.74276,0.36261 0.456184,0 0.736912,-0.36261 0.286577,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105273,-0.56731 -0.39185,-0.92991 -0.280728,-0.36846 -0.736912,-0.36846 -0.456183,0 -0.74276,0.36846 -0.280728,0.3626 -0.386002,0.92991 z m -1.421187,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.23394,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666824,-0.61409 1.017641,0 1.666825,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666825,0.60825 -1.01764,0 -1.666824,-0.60825 -0.649185,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1728"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 69.050828,129.08057 q -0.08773,0.47958 -0.08773,1.50891 0,1.02934 0.08773,1.50892 0.105273,0.5673 0.386001,0.93576 0.286577,0.36261 0.742761,0.36261 0.456183,0 0.736912,-0.36261 0.286576,-0.36846 0.39185,-0.93576 0.08773,-0.48543 0.08773,-1.50892 0,-1.02933 -0.08773,-1.50891 -0.105274,-0.56731 -0.39185,-0.92991 -0.280729,-0.36846 -0.736912,-0.36846 -0.456184,0 -0.742761,0.36846 -0.280728,0.3626 -0.386001,0.92991 z m -1.421188,3.55004 q -0.163758,-0.64918 -0.163758,-2.04113 0,-1.39194 0.163758,-2.04113 0.233941,-0.94745 0.883125,-1.5557 0.649184,-0.61409 1.666825,-0.61409 1.01764,0 1.666824,0.61409 0.649185,0.60825 0.883125,1.5557 0.163758,0.64919 0.163758,2.04113 0,1.39195 -0.163758,2.04113 -0.23394,0.94746 -0.883125,1.56155 -0.649184,0.60825 -1.666824,0.60825 -1.017641,0 -1.666825,-0.60825 -0.649184,-0.61409 -0.883125,-1.56155 z" />
- <path
- id="path1730"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 73.916786,134.70683 v -8.2347 h 1.497218 v 6.83106 h 3.368741 v 1.40364 z" />
- <path
- id="path1732"
- style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8.98330784px;font-family:Monoid;-inkscape-font-specification:'Monoid Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#e5b62f;fill-opacity:1;stroke-width:0.22458273"
- d="m 82.028666,134.80041 q -0.959155,0 -2.163948,-0.18715 v -1.42704 q 1.40949,0.21055 2.163948,0.21055 1.041034,0 1.041034,-1.11122 0,-0.30997 -0.152061,-0.58485 -0.146213,-0.28073 -0.415244,-0.386 -0.169607,-0.0643 -0.467881,-0.10527 -0.298274,-0.0409 -0.614093,-0.11697 -0.309971,-0.0819 -0.596548,-0.25149 -1.099519,-0.65503 -1.099519,-2.07037 0,-1.01179 0.643336,-1.70192 0.649184,-0.69012 1.660976,-0.69012 0.789548,0 2.163948,0.14621 v 1.40949 q -1.421188,-0.15206 -2.163948,-0.15206 -0.222243,0 -0.39185,0.0994 -0.163758,0.0994 -0.251486,0.25734 -0.08773,0.15791 -0.128667,0.32167 -0.03509,0.16375 -0.03509,0.32166 0,0.69013 0.46788,0.90067 0.152062,0.0702 0.690124,0.15791 0.543911,0.0819 0.877276,0.25734 0.467881,0.23979 0.772003,0.61994 0.304123,0.38015 0.421093,0.76616 0.11697,0.386 0.11697,0.80124 0,1.13461 -0.701821,1.82474 -0.701821,0.69012 -1.836431,0.69012 z" />
- </g>
- </g>
+<?xml version="1.0" encoding="UTF-8"?>
+<svg id="svg1021" width="60.537mm" height="56.933mm" version="1.1" viewBox="0 0 60.537 56.933" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <metadata id="metadata1018">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(-24.954 -78.736)">
+ <g id="g1769">
+ <g id="g1751">
+ <path id="rect934" d="m56.864 87.986c-1.2455-0.01183-3.6018 0.03564-3.6018 0.03564-0.11429 0-0.20619 0.09589-0.20619 0.21498v1.4624h-2.1461v-1.4624c0-0.1191-0.0919-0.21498-0.20619-0.21498h-2.559c-0.11429 0-0.2067 0.09589-0.2067 0.21498v5.4188c0 0.1191 0.09235 0.21498 0.2067 0.21498h2.559c0.11429 0 0.20619-0.09587 0.20619-0.21498v-1.4619h2.1461v1.4619c0 0.1191 0.09191 0.21498 0.20619 0.21498h1.295v6.8952c-0.74253 0.29471-1.2656 1.0166-1.2656 1.8666v10.45c0 1.1133 0.89588 2.0092 2.0092 2.0092 1.1133 0 2.0097-0.89589 2.0097-2.0092v-10.45c0-0.84994-0.52302-1.5718-1.2656-1.8666v-6.8952h1.4025c0.2234-0.19668 0.4387-0.98538 1.6319-1.5885 3.2043-1.1007 4.5957 1.626 4.5713 1.2495-0.02611-0.40191-0.20364-2.9223-3.0014-4.4008-1.2189-0.54811-2.652-1.0866-3.0386-1.109-0.03989-0.02102-0.33157-0.03172-0.74672-0.03564z" fill="#e5b62f" stroke="#e5b62f" stroke-width=".47745" style="paint-order:markers fill stroke"/>
+ <path id="path944" d="m59.665 79.605c-1.0993 0.01828-1.9696 0.47364-2.4975 0.8661-0.70383 0.52328-0.92957 0.82263-1.9942 0.82528-1.0646 0.0027-1.2909-0.2962-1.9973-0.81597-0.70642-0.51977-2.0227-1.1467-3.6726-0.70022-1.65 0.44651-2.47 1.6516-2.8179 2.4567-0.3479 0.80508-0.39346 1.1774-1.3141 1.712-0.92067 0.53461-1.2667 0.38993-2.1384 0.29301-0.87166-0.09692-2.3259 0.0172-3.5316 1.2289-1.2057 1.2117-1.3141 2.6671-1.2128 3.5383 0.10123 0.87118 0.24785 1.2161-0.28215 2.1394-0.53002 0.92331-0.90043 0.96999-1.7038 1.3219s-2.006 1.1771-2.4443 2.8293c-0.4383 1.6522 0.19712 2.9652 0.72037 3.669 0.52326 0.70383 0.82262 0.92957 0.82527 1.9942 0.0026 1.0646-0.29621 1.2904-0.81597 1.9968-0.51977 0.70643-1.1462 2.0227-0.6997 3.6726s1.6511 2.4705 2.4562 2.8184c0.80508 0.3479 1.1774 0.39347 1.712 1.3141 0.5346 0.92066 0.38993 1.2662 0.29301 2.1378-0.09692 0.87166 0.01712 2.3264 1.2289 3.5321 1.2117 1.2056 2.6671 1.311 3.5383 1.2098 0.87117-0.10126 1.2161-0.24477 2.1394 0.28525 0.92331 0.53002 0.96998 0.90043 1.3219 1.7038 0.3519 0.80334 1.1771 2.0055 2.8293 2.4438 1.6522 0.43829 3.678-0.39906 3.669-0.72037-0.0091-0.3213-1.3658 0.23152-2.7259-0.62425s-1.2623-1.3841-1.5622-2.0686c-0.29986-0.68453-0.83626-1.9698-2.3327-2.8288-1.4964-0.859-2.8761-0.67281-3.6184-0.58653-0.74233 0.0863-0.79194 0.23675-1.558-0.52555-0.7661-0.76231-0.6133-0.81116-0.53072-1.5539 0.08259-0.74275 0.26027-2.1221-0.60616-3.6142-0.86643-1.4921-2.1531-2.0228-2.8391-2.3192s-0.80341-0.18822-1.0857-1.2314-0.12923-1.0134 0.31368-1.6154c0.44289-0.60195 1.289-1.7064 1.2847-3.4318-0.0042-1.7254-0.85585-2.829-1.3017-3.4287-0.44588-0.59974-0.60063-0.56665-0.32349-1.6113 0.27712-1.0446 0.39654-0.93676 1.0811-1.2366 0.68454-0.29985 1.9698-0.83626 2.8288-2.3327 0.85899-1.4964 0.67281-2.8761 0.58653-3.6184-0.08624-0.74233-0.23624-0.79194 0.52606-1.558 0.76231-0.76611 0.81064-0.6164 1.5534-0.53382 0.74274 0.08258 2.1221 0.26337 3.6142-0.60306 1.4921-0.86643 2.0202-2.1531 2.3167-2.8391 0.29645-0.68602 0.19132-0.80341 1.2345-1.0857 1.0432-0.28231 1.0129-0.12921 1.6149 0.31368 0.60196 0.44289 1.7064 1.289 3.4318 1.2847 1.7254-0.0043 2.8259-0.85533 3.4256-1.3012 0.59974-0.44589 0.56717-0.60113 1.6118-0.32401 1.0446 0.27712 0.93934 0.39654 1.2392 1.0811 0.29986 0.68453 0.83678 1.9698 2.3332 2.8288 1.4964 0.859 2.8755 0.67332 3.6179 0.58704 0.74233-0.08628 0.79194-0.23676 1.558 0.52555 0.76611 0.7623 0.61382 0.81064 0.53124 1.5534-0.08258 0.74275-0.26078 2.1221 0.60565 3.6142 0.86643 1.4921 2.1531 2.0202 2.8391 2.3167 0.68602 0.29645 0.80392 0.19132 1.0862 1.2346 0.28231 1.0432 0.1287 1.0129-0.31419 1.6149-0.4429 0.60195-1.289 1.7064-1.2847 3.4318 0.0043 1.7254 0.85584 2.8259 1.3017 3.4256 0.44588 0.59974 0.60062 0.56717 0.3235 1.6118-0.27712 1.0446-0.39654 0.93986-1.0811 1.2397-0.68454 0.29985-1.9698 0.83626-2.8288 2.3327-0.859 1.4964-0.6728 2.8755-0.58653 3.6179 0.08628 0.74233 0.23676 0.79194-0.52555 1.558-0.76231 0.76611-0.81116 0.61382-1.5539 0.53124-0.74275-0.0826-2.1221-0.26027-3.6143 0.60616-1.4921 0.86644-2.0233 2.1531-2.3198 2.8391-0.24633 0.57005-0.22224 0.74776-0.80254 0.95343-0.08286-0.046-0.17925-0.0898-0.29352-0.1328-0.96214-0.36199-1.0882 0.12751-1.8779 0.71571-0.71013 0.52895-1.2184 0.5911-1.1886 1.2888-0.06706-7e-3 -0.13033-8e-3 -0.18087-2e-3 -0.16123 0.0187-0.22496 0.0454-0.39584-0.0527-0.17088-0.0981-0.1793-0.16656-0.24443-0.31522-0.06513-0.14868-0.21773-0.37158-0.52348-0.45269-0.30577-0.0811-0.6807 0.0739-0.67903 0.13332 0.0016 0.0594 0.25266-0.0426 0.50436 0.11576 0.25171 0.15837 0.23338 0.25624 0.28887 0.38292 0.05548 0.1267 0.15508 0.36451 0.43202 0.52349 0.27693 0.15896 0.53183 0.12449 0.66921 0.10851 0.1049-0.0122 0.13623-0.0302 0.20774 0.0243 0.21075 0.72266 0.72116 0.6429 1.6056 0.97565 0.96214 0.36199 1.2562 0.7975 2.0459 0.20929 0.57854-0.43092 0.52042-0.74207 0.38086-1.25 1.2857-0.53767 1.9604-1.5578 2.267-2.2676 0.3479-0.80508 0.39347-1.1769 1.3141-1.7115 0.92066-0.5346 1.2662-0.39043 2.1379-0.29351 0.87166 0.0969 2.3264-0.0167 3.5321-1.2284s1.3136-2.6676 1.2123-3.5388c-0.10126-0.87117-0.24735-1.2156 0.28267-2.1389 0.53002-0.92331 0.90043-0.9705 1.7038-1.3224 0.80334-0.35189 2.0055-1.1766 2.4438-2.8288 0.43829-1.6522-0.1971-2.9652-0.72037-3.669-0.52328-0.70383-0.82211-0.92957-0.82476-1.9942-0.0026-1.0646 0.2962-1.2909 0.81597-1.9973 0.51977-0.70642 1.1462-2.0227 0.6997-3.6726-0.4465-1.65-1.6516-2.47-2.4567-2.8179-0.80508-0.3479-1.1769-0.39346-1.7115-1.3141-0.5346-0.92066-0.39044-1.2667-0.29352-2.1384 0.09692-0.87167-0.0167-2.3259-1.2283-3.5316-1.2117-1.2056-2.6676-1.3141-3.5388-1.2128-0.87117 0.10126-1.2156 0.24786-2.1389-0.28215-0.92331-0.53002-0.9705-0.90044-1.3224-1.7038-0.3519-0.8033-1.1766-2.006-2.8288-2.4442-0.41304-0.10957-0.80507-0.15182-1.1715-0.14573z" fill="#336790" style="paint-order:markers fill stroke"/>
+ </g>
+ <g id="g1747" stroke-width=".22458">
+ <g id="text1023" fill="#336790" aria-label="SETUP">
+ <path id="path1713" d="m28.181 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1715" d="m32.036 126.47h4.4917v1.4036h-2.9944v1.9885h2.2458v1.4036h-2.2458v2.0353h2.9944v1.4036h-4.4917z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1717" d="m42.885 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1719" d="m43.627 126.47h1.4972v5.2403q0 0.54976 0.04094 0.74861 0.08773 0.42109 0.3743 0.67842 0.29242 0.25734 0.70767 0.25734t0.70182-0.25734q0.29242-0.25733 0.38015-0.67842 0.04094-0.19885 0.04094-0.74861v-5.2403h1.4972v5.2403q0 0.82464-0.09358 1.2223-0.19885 0.82463-0.88897 1.3452-0.68428 0.52052-1.6376 0.52052t-1.6434-0.52052q-0.68428-0.52052-0.88312-1.3452-0.09358-0.3977-0.09358-1.2223z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1721" d="m51.482 127.88v2.3862h0.36261q0.50297 0 0.73106-0.0468 0.3977-0.0819 0.63164-0.39185 0.23979-0.30997 0.23979-0.75446t-0.23979-0.75446q-0.23394-0.30997-0.63164-0.39185-0.22809-0.0468-0.73106-0.0468zm-1.4972-1.4036h1.8481q0.86558 0 1.234 0.0877 0.83049 0.19885 1.351 0.87143 0.52636 0.66673 0.52636 1.6376t-0.52636 1.6434q-0.52052 0.66673-1.351 0.86558-0.36846 0.0877-1.234 0.0877h-0.35091v3.0412h-1.4972z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
</g>
+ <g id="text1661" fill="#e5b62f" aria-label="TOOLS">
+ <path id="path1724" d="m60.834 126.47v1.4036h-1.8715v6.831h-1.4972v-6.831h-1.8715v-1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1726" d="m63.068 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1728" d="m69.051 129.08q-0.08773 0.47958-0.08773 1.5089 0 1.0293 0.08773 1.5089 0.10527 0.5673 0.386 0.93576 0.28658 0.36261 0.74276 0.36261 0.45618 0 0.73691-0.36261 0.28658-0.36846 0.39185-0.93576 0.08773-0.48543 0.08773-1.5089 0-1.0293-0.08773-1.5089-0.10527-0.56731-0.39185-0.92991-0.28073-0.36846-0.73691-0.36846-0.45618 0-0.74276 0.36846-0.28073 0.3626-0.386 0.92991zm-1.4212 3.55q-0.16376-0.64918-0.16376-2.0411 0-1.3919 0.16376-2.0411 0.23394-0.94745 0.88312-1.5557 0.64918-0.61409 1.6668-0.61409 1.0176 0 1.6668 0.61409 0.64918 0.60825 0.88312 1.5557 0.16376 0.64919 0.16376 2.0411 0 1.392-0.16376 2.0411-0.23394 0.94746-0.88312 1.5616-0.64918 0.60825-1.6668 0.60825-1.0176 0-1.6668-0.60825-0.64918-0.61409-0.88312-1.5616z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1730" d="m73.917 134.71v-8.2347h1.4972v6.8311h3.3687v1.4036z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ <path id="path1732" d="m82.029 134.8q-0.95916 0-2.1639-0.18715v-1.427q1.4095 0.21055 2.1639 0.21055 1.041 0 1.041-1.1112 0-0.30997-0.15206-0.58485-0.14621-0.28073-0.41524-0.386-0.16961-0.0643-0.46788-0.10527-0.29827-0.0409-0.61409-0.11697-0.30997-0.0819-0.59655-0.25149-1.0995-0.65503-1.0995-2.0704 0-1.0118 0.64334-1.7019 0.64918-0.69012 1.661-0.69012 0.78955 0 2.1639 0.14621v1.4095q-1.4212-0.15206-2.1639-0.15206-0.22224 0-0.39185 0.0994-0.16376 0.0994-0.25149 0.25734-0.08773 0.15791-0.12867 0.32167-0.03509 0.16375-0.03509 0.32166 0 0.69013 0.46788 0.90067 0.15206 0.0702 0.69012 0.15791 0.54391 0.0819 0.87728 0.25734 0.46788 0.23979 0.772 0.61994 0.30412 0.38015 0.42109 0.76616 0.11697 0.386 0.11697 0.80124 0 1.1346-0.70182 1.8247-0.70182 0.69012-1.8364 0.69012z" style="font-feature-settings:normal;font-variant-caps:normal;font-variant-ligatures:normal;font-variant-numeric:normal"/>
+ </g>
+ </g>
</g>
+ </g>
</svg>
diff --git a/docs/index.rst b/docs/index.rst
index b886c8f8..0f52c360 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -26,6 +26,6 @@ designed to facilitate packaging Python projects.
Development guide <development/index>
Backward compatibility & deprecated practice <deprecated/index>
Changelog <history>
- Artwork <images/README>
+ artwork
.. tidelift-referral-banner::
diff --git a/docs/pkg_resources.rst b/docs/pkg_resources.rst
index fb5fc077..c1158189 100644
--- a/docs/pkg_resources.rst
+++ b/docs/pkg_resources.rst
@@ -1151,7 +1151,7 @@ paths.
will be read as-is.
``resource_string(package_or_requirement, resource_name)``
- Return the specified resource as a string. The resource is read in
+ Return the specified resource as ``bytes``. The resource is read in
binary fashion, such that the returned string contains exactly the bytes
that are stored in the resource.
diff --git a/docs/setuptools.rst b/docs/setuptools.rst
index c5a89adc..d0fb9a9c 100644
--- a/docs/setuptools.rst
+++ b/docs/setuptools.rst
@@ -201,13 +201,13 @@ As a consequence, the resulting dictionary will include no such options.
-Mailing List and Bug Tracker
-============================
+Forum and Bug Tracker
+=====================
-Please use the `distutils-sig mailing list`_ for questions and discussion about
+Please use `GitHub Discussions`_ for questions and discussion about
setuptools, and the `setuptools bug tracker`_ ONLY for issues you have
-confirmed via the list are actual bugs, and which you have reduced to a minimal
+confirmed via the forum are actual bugs, and which you have reduced to a minimal
set of steps to reproduce.
-.. _distutils-sig mailing list: http://mail.python.org/pipermail/distutils-sig/
+.. _GitHub Discussions: https://github.com/pypa/setuptools/discussions
.. _setuptools bug tracker: https://github.com/pypa/setuptools/
diff --git a/docs/userguide/declarative_config.rst b/docs/userguide/declarative_config.rst
index d1c25df1..6f41d92b 100644
--- a/docs/userguide/declarative_config.rst
+++ b/docs/userguide/declarative_config.rst
@@ -222,10 +222,10 @@ data_files section 40.6.0 [#
.. [#opt-1] In the ``package_data`` section, a key named with a single asterisk
(``*``) refers to all packages, in lieu of the empty string used in ``setup.py``.
-
+
.. [#opt-2] In the ``extras_require`` section, values are parsed as ``list-semi``.
This implies that in order to include markers, they **must** be *dangling*:
-
+
.. code-block:: ini
[options.extras_require]
diff --git a/docs/userguide/dependency_management.rst b/docs/userguide/dependency_management.rst
index 23578a57..ea2fc556 100644
--- a/docs/userguide/dependency_management.rst
+++ b/docs/userguide/dependency_management.rst
@@ -28,7 +28,7 @@ other two types of dependency keyword, this one is specified in your
.. code-block:: ini
[build-system]
- requires = ["setuptools", "wheel"]
+ requires = ["setuptools"]
#...
.. note::
@@ -360,6 +360,6 @@ or ``setup.py``.
setup(
name="Project-B",
- python_requires=[">=3.6"],
+ python_requires=">=3.6",
...,
)
diff --git a/docs/userguide/distribution.rst b/docs/userguide/distribution.rst
index 2872dacd..db0f1a5f 100644
--- a/docs/userguide/distribution.rst
+++ b/docs/userguide/distribution.rst
@@ -162,7 +162,7 @@ Specifying Your Project's Version
---------------------------------
Setuptools can work well with most versioning schemes. Over the years,
-setuptools has tried to closely follow the
+setuptools has tried to closely follow the
`PEP 440 <https://www.python.org/dev/peps/pep-0440/>`_ scheme, but it
also supports legacy versions. There are, however, a
few special things to watch out for, in order to ensure that setuptools and
diff --git a/docs/userguide/quickstart.rst b/docs/userguide/quickstart.rst
index 6bf353a0..203d6204 100644
--- a/docs/userguide/quickstart.rst
+++ b/docs/userguide/quickstart.rst
@@ -14,9 +14,9 @@ Python packaging at a glance
============================
The landscape of Python packaging is shifting and ``Setuptools`` has evolved to
only provide backend support, no longer being the de-facto packaging tool in
-the market. All python package must provide a ``pyproject.toml`` and specify
+the market. Every python package must provide a ``pyproject.toml`` and specify
the backend (build system) it wants to use. The distribution can then
-be generated with whatever tools that provides a ``build sdist``-alike
+be generated with whatever tool that provides a ``build sdist``-like
functionality. While this may appear cumbersome, given the added pieces,
it in fact tremendously enhances the portability of your package. The
change is driven under :pep:`PEP 517 <517#build-requirements>`. To learn more about Python packaging in general,
@@ -32,7 +32,7 @@ package your project:
.. code-block:: toml
[build-system]
- requires = ["setuptools", "wheel"]
+ requires = ["setuptools"]
build-backend = "setuptools.build_meta"
Then, you will need a ``setup.cfg`` or ``setup.py`` to specify your package
@@ -76,7 +76,7 @@ This is what your project would look like::
setup.cfg # or setup.py
mypackage/__init__.py
-Then, you need an builder, such as :std:doc:`PyPA build <pypa-build:index>`
+Then, you need a builder, such as :std:doc:`PyPA build <pypa-build:index>`
which you can obtain via ``pip install build``. After downloading it, invoke
the builder::
@@ -89,15 +89,15 @@ Of course, before you release your project to PyPI, you'll want to add a bit
more information to your setup script to help people find or learn about your
project. And maybe your project will have grown by then to include a few
dependencies, and perhaps some data files and scripts. In the next few sections,
-we will walk through those additional but essential information you need
+we will walk through the additional but essential information you need
to specify to properly package your project.
Automatic package discovery
===========================
For simple projects, it's usually easy enough to manually add packages to
-the ``packages`` keyword in ``setup.cfg``. However, for very large projects
-, it can be a big burden to keep the package list updated. ``setuptools``
+the ``packages`` keyword in ``setup.cfg``. However, for very large projects,
+it can be a big burden to keep the package list updated. ``setuptools``
therefore provides two convenient tools to ease the burden: :literal:`find:\ ` and
:literal:`find_namespace:\ `. To use it in your project:
@@ -110,11 +110,11 @@ therefore provides two convenient tools to ease the burden: :literal:`find:\ ` a
include=pkg1, pkg2
exclude=pk3, pk4
-When you pass the above information, alongside other necessary ones,
+When you pass the above information, alongside other necessary information,
``setuptools`` walks through the directory specified in ``where`` (omitted
-here as the package reside in current directory) and filters the packages
-it can find following the ``include`` (default to none), then remove
-those that match the ``exclude`` and return a list of Python packages. Note
+here as the package resides in the current directory) and filters the packages
+it can find following the ``include`` (defaults to none), then removes
+those that match the ``exclude`` and returns a list of Python packages. Note
that each entry in the ``[options.packages.find]`` is optional. The above
setup also allows you to adopt a ``src/`` layout. For more details and advanced
use, go to :ref:`package_discovery`
@@ -122,7 +122,7 @@ use, go to :ref:`package_discovery`
Entry points and automatic script creation
===========================================
-Setuptools support automatic creation of scripts upon installation, that runs
+Setuptools supports automatic creation of scripts upon installation, that runs
code within your package if you specify them with the ``entry_points`` keyword.
This is what allows you to run commands like ``pip install`` instead of having
to type ``python -m pip install``. To accomplish this, add the entry_points
@@ -157,9 +157,9 @@ operators <, >, <=, >=, == or !=, followed by a version identifier):
When your project is installed, all of the dependencies not already installed
will be located (via PyPI), downloaded, built (if necessary), and installed.
-This, of course, is a simplified scenarios. ``setuptools`` also provide
+This, of course, is a simplified scenarios. ``setuptools`` also provides
additional keywords such as ``setup_requires`` that allows you to install
-dependencies before running the script, and ``extras_requires`` that take
+dependencies before running the script, and ``extras_require`` that take
care of those needed by automatically generated scripts. It also provides
mechanisms to handle dependencies that are not in PyPI. For more advanced use,
see :doc:`dependency_management`
@@ -189,9 +189,9 @@ Development mode
.. tip::
- Prior to :ref:`pip v21.1 <pip:v21-1>`, a ``setup.py`` script was
- required to be compatible with development mode. With late
- versions of pip, any project may be installed in this mode.
+ Prior to :ref:`pip v21.1 <pip:v21-1>`, a ``setup.py`` script was
+ required to be compatible with development mode. With late
+ versions of pip, any project may be installed in this mode.
``setuptools`` allows you to install a package without copying any files
to your interpreter directory (e.g. the ``site-packages`` directory).
@@ -207,7 +207,7 @@ associate with your source code. For more information, see :doc:`development_mod
Uploading your package to PyPI
==============================
-After generating the distribution files, next step would be to upload your
+After generating the distribution files, the next step would be to upload your
distribution so others can use it. This functionality is provided by
`twine <https://pypi.org/project/twine/>`_ and we will only demonstrate the
basic use here.
@@ -225,5 +225,6 @@ parsed by ``setuptool`` to ease the pain of transition.
Resources on Python packaging
=============================
-Packaging in Python is hard. Here we provide a list of links for those that
-want to learn more.
+Packaging in Python can be hard and is constantly evolving.
+`Python Packaging User Guide <https://packaging.python.org>`_ has tutorials and
+up-to-date references that can help you when it is time to distribute your work.
diff --git a/exercises.py b/exercises.py
new file mode 100644
index 00000000..76176be5
--- /dev/null
+++ b/exercises.py
@@ -0,0 +1,6 @@
+def measure_startup_perf():
+ # run by pytest_perf
+ import subprocess
+ import sys # end warmup
+
+ subprocess.check_call([sys.executable, '-c', 'pass'])
diff --git a/pavement.py b/pavement.py
deleted file mode 100644
index 81ff6f12..00000000
--- a/pavement.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import re
-import sys
-import subprocess
-
-from paver.easy import task, path as Path
-
-
-def remove_all(paths):
- for path in paths:
- path.rmtree() if path.isdir() else path.remove()
-
-
-@task
-def update_vendored():
- update_pkg_resources()
- update_setuptools()
-
-
-def rewrite_packaging(pkg_files, new_root):
- """
- Rewrite imports in packaging to redirect to vendored copies.
- """
- for file in pkg_files.glob('*.py'):
- text = file.text()
- text = re.sub(r' (pyparsing)', rf' {new_root}.\1', text)
- text = text.replace(
- 'from six.moves.urllib import parse',
- 'from urllib import parse',
- )
- file.write_text(text)
-
-
-def clean(vendor):
- """
- Remove all files out of the vendor directory except the meta
- data (as pip uninstall doesn't support -t).
- """
- remove_all(
- path
- for path in vendor.glob('*')
- if path.basename() != 'vendored.txt'
- )
-
-
-def install(vendor):
- clean(vendor)
- install_args = [
- sys.executable,
- '-m', 'pip',
- 'install',
- '-r', str(vendor / 'vendored.txt'),
- '-t', str(vendor),
- ]
- subprocess.check_call(install_args)
- remove_all(vendor.glob('*.dist-info'))
- remove_all(vendor.glob('*.egg-info'))
- remove_all(vendor.glob('six.py'))
- (vendor / '__init__.py').write_text('')
-
-
-def update_pkg_resources():
- vendor = Path('pkg_resources/_vendor')
- install(vendor)
- rewrite_packaging(vendor / 'packaging', 'pkg_resources.extern')
-
-
-def update_setuptools():
- vendor = Path('setuptools/_vendor')
- install(vendor)
- rewrite_packaging(vendor / 'packaging', 'setuptools.extern')
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index a37c0479..e7e31b9f 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -70,6 +70,12 @@ try:
except ImportError:
importlib_machinery = None
+from pkg_resources.extern.jaraco.text import (
+ yield_lines,
+ drop_comment,
+ join_continuation,
+)
+
from pkg_resources.extern import appdirs
from pkg_resources.extern import packaging
__import__('pkg_resources.extern.packaging.version')
@@ -1474,7 +1480,7 @@ class NullProvider:
def _validate_resource_path(path):
"""
Validate the resource paths according to the docs.
- https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access
+ https://setuptools.pypa.io/en/latest/pkg_resources.html#basic-resource-access
>>> warned = getfixture('recwarn')
>>> warnings.simplefilter('always')
@@ -1571,7 +1577,7 @@ class EggProvider(NullProvider):
"""Provider based on a virtual filesystem"""
def __init__(self, module):
- NullProvider.__init__(self, module)
+ super().__init__(module)
self._setup_prefix()
def _setup_prefix(self):
@@ -1691,7 +1697,7 @@ class ZipProvider(EggProvider):
_zip_manifests = MemoizedZipManifests()
def __init__(self, module):
- EggProvider.__init__(self, module)
+ super().__init__(module)
self.zip_pre = self.loader.archive + os.sep
def _zipinfo_name(self, fspath):
@@ -2150,12 +2156,14 @@ def _handle_ns(packageName, path_item):
# use find_spec (PEP 451) and fall-back to find_module (PEP 302)
try:
- loader = importer.find_spec(packageName).loader
+ 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
@@ -2341,20 +2349,6 @@ def _set_parent_ns(packageName):
setattr(sys.modules[parent], name, sys.modules[packageName])
-def yield_lines(strs):
- """Yield non-empty/non-comment lines of a string or sequence"""
- if isinstance(strs, str):
- for s in strs.splitlines():
- s = s.strip()
- # skip blank lines/comments
- if s and not s.startswith('#'):
- yield s
- else:
- for ss in strs:
- for s in yield_lines(ss):
- yield s
-
-
MODULE = re.compile(r"\w+(\.\w+)*$").match
EGG_NAME = re.compile(
r"""
@@ -2965,12 +2959,12 @@ class DistInfoDistribution(Distribution):
if not req.marker or req.marker.evaluate({'extra': extra}):
yield req
- common = frozenset(reqs_for_extra(None))
+ 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] = list(frozenset(reqs_for_extra(extra)) - common)
+ dm[s_extra] = [r for r in reqs_for_extra(extra) if r not in common]
return dm
@@ -2996,25 +2990,12 @@ def issue_warning(*args, **kw):
def parse_requirements(strs):
- """Yield ``Requirement`` objects for each specification in `strs`
+ """
+ Yield ``Requirement`` objects for each specification in `strs`.
`strs` must be a string, or a (possibly-nested) iterable thereof.
"""
- # create a steppable iterator, so we can handle \-continuations
- lines = iter(yield_lines(strs))
-
- for line in lines:
- # Drop comments -- a hash without a space may be in a URL.
- if ' #' in line:
- line = line[:line.find(' #')]
- # If there is a line continuation, drop it, and append the next line.
- if line.endswith('\\'):
- line = line[:-2].strip()
- try:
- line += next(lines)
- except StopIteration:
- return
- yield Requirement(line)
+ return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs))))
class RequirementParseError(packaging.requirements.InvalidRequirement):
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/DESCRIPTION.rst b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/DESCRIPTION.rst
new file mode 100644
index 00000000..c605ec26
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/DESCRIPTION.rst
@@ -0,0 +1,227 @@
+
+.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png
+ :target: http://travis-ci.org/ActiveState/appdirs
+
+the problem
+===========
+
+What directory should your app use for storing user data? If running on Mac OS X, you
+should use::
+
+ ~/Library/Application Support/<AppName>
+
+If on Windows (at least English Win XP) that should be::
+
+ C:\Documents and Settings\<User>\Application Data\Local Settings\<AppAuthor>\<AppName>
+
+or possibly::
+
+ C:\Documents and Settings\<User>\Application Data\<AppAuthor>\<AppName>
+
+for `roaming profiles <http://bit.ly/9yl3b6>`_ but that is another story.
+
+On Linux (and other Unices) the dir, according to the `XDG
+spec <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_, is::
+
+ ~/.local/share/<AppName>
+
+
+``appdirs`` to the rescue
+=========================
+
+This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will
+help you choose an appropriate:
+
+- user data dir (``user_data_dir``)
+- user config dir (``user_config_dir``)
+- user cache dir (``user_cache_dir``)
+- site data dir (``site_data_dir``)
+- site config dir (``site_config_dir``)
+- user log dir (``user_log_dir``)
+
+and also:
+
+- is a single module so other Python packages can include their own private copy
+- is slightly opinionated on the directory names used. Look for "OPINION" in
+ documentation and code for when an opinion is being applied.
+
+
+some example output
+===================
+
+On Mac OS X::
+
+ >>> from appdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ '/Users/trentm/Library/Application Support/SuperApp'
+ >>> site_data_dir(appname, appauthor)
+ '/Library/Application Support/SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ '/Users/trentm/Library/Caches/SuperApp'
+ >>> user_log_dir(appname, appauthor)
+ '/Users/trentm/Library/Logs/SuperApp'
+
+On Windows 7::
+
+ >>> from appdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
+ >>> user_data_dir(appname, appauthor, roaming=True)
+ 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache'
+ >>> user_log_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs'
+
+On Linux::
+
+ >>> from appdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ '/home/trentm/.local/share/SuperApp
+ >>> site_data_dir(appname, appauthor)
+ '/usr/local/share/SuperApp'
+ >>> site_data_dir(appname, appauthor, multipath=True)
+ '/usr/local/share/SuperApp:/usr/share/SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ '/home/trentm/.cache/SuperApp'
+ >>> user_log_dir(appname, appauthor)
+ '/home/trentm/.cache/SuperApp/log'
+ >>> user_config_dir(appname)
+ '/home/trentm/.config/SuperApp'
+ >>> site_config_dir(appname)
+ '/etc/xdg/SuperApp'
+ >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc'
+ >>> site_config_dir(appname, multipath=True)
+ '/etc/SuperApp:/usr/local/etc/SuperApp'
+
+
+``AppDirs`` for convenience
+===========================
+
+::
+
+ >>> from appdirs import AppDirs
+ >>> dirs = AppDirs("SuperApp", "Acme")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp'
+ >>> dirs.site_data_dir
+ '/Library/Application Support/SuperApp'
+ >>> dirs.user_cache_dir
+ '/Users/trentm/Library/Caches/SuperApp'
+ >>> dirs.user_log_dir
+ '/Users/trentm/Library/Logs/SuperApp'
+
+
+
+Per-version isolation
+=====================
+
+If you have multiple versions of your app in use that you want to be
+able to run side-by-side, then you may want version-isolation for these
+dirs::
+
+ >>> from appdirs import AppDirs
+ >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp/1.0'
+ >>> dirs.site_data_dir
+ '/Library/Application Support/SuperApp/1.0'
+ >>> dirs.user_cache_dir
+ '/Users/trentm/Library/Caches/SuperApp/1.0'
+ >>> dirs.user_log_dir
+ '/Users/trentm/Library/Logs/SuperApp/1.0'
+
+
+
+appdirs Changelog
+=================
+
+appdirs 1.4.3
+-------------
+- [PR #76] Python 3.6 invalid escape sequence deprecation fixes
+- Fix for Python 3.6 support
+
+appdirs 1.4.2
+-------------
+- [PR #84] Allow installing without setuptools
+- [PR #86] Fix string delimiters in setup.py description
+- Add Python 3.6 support
+
+appdirs 1.4.1
+-------------
+- [issue #38] Fix _winreg import on Windows Py3
+- [issue #55] Make appname optional
+
+appdirs 1.4.0
+-------------
+- [PR #42] AppAuthor is now optional on Windows
+- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows
+ support requires `JNA <https://github.com/twall/jna>`_.
+- [PR #44] Fix incorrect behaviour of the site_config_dir method
+
+appdirs 1.3.0
+-------------
+- [Unix, issue 16] Conform to XDG standard, instead of breaking it for
+ everybody
+- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are
+ usually case sensitive, so mangling is not wise
+- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result
+ based on XDG_DATA_DIRS and make room for respecting the standard which
+ specifies XDG_DATA_DIRS is a multiple-value variable
+- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to
+ XDG specs; on Windows and Mac return the corresponding ``*_data_dir``
+
+appdirs 1.2.0
+-------------
+
+- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more
+ typical.
+- [issue 9] Make ``unicode`` work on py3k.
+
+appdirs 1.1.0
+-------------
+
+- [issue 4] Add ``AppDirs.user_log_dir``.
+- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec
+ <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_.
+- [Mac, issue 5] Fix ``site_data_dir()`` on Mac.
+- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports
+ Python3 now.
+- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use
+ ``opinion=False`` option to disable this.
+- Add ``appdirs.AppDirs`` convenience class. Usage:
+
+ >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp/1.0'
+
+- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short
+ paths if there are high bit chars.
+- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g.
+ "~/.superapp/cache".
+- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only)
+ and change the default ``user_data_dir`` behaviour to use a *non*-roaming
+ profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because
+ a large roaming profile can cause login speed issues. The "only syncs on
+ logout" behaviour can cause surprises in appdata info.
+
+
+appdirs 1.0.1 (never released)
+------------------------------
+
+Started this changelog 27 July 2010. Before that this module originated in the
+`Komodo <http://www.activestate.com/komodo>`_ product as ``applib.py`` and then
+as `applib/location.py
+<http://github.com/ActiveState/applib/blob/master/applib/location.py>`_ (used by
+`PyPM <http://code.activestate.com/pypm/>`_ in `ActivePython
+<http://www.activestate.com/activepython>`_). This is basically a fork of
+applib.py 1.0.1 and applib/location.py 1.0.1.
+
+
+
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/INSTALLER b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/METADATA b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/METADATA
new file mode 100644
index 00000000..69ddf934
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/METADATA
@@ -0,0 +1,254 @@
+Metadata-Version: 2.0
+Name: appdirs
+Version: 1.4.3
+Summary: A small Python module for determining appropriate platform-specific dirs, e.g. a "user data dir".
+Home-page: http://github.com/ActiveState/appdirs
+Author: Trent Mick; Sridhar Ratnakumar; Jeff Rouse
+Author-email: trentm@gmail.com; github@srid.name; jr@its.to
+License: MIT
+Keywords: application directory log cache user
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+
+
+.. image:: https://secure.travis-ci.org/ActiveState/appdirs.png
+ :target: http://travis-ci.org/ActiveState/appdirs
+
+the problem
+===========
+
+What directory should your app use for storing user data? If running on Mac OS X, you
+should use::
+
+ ~/Library/Application Support/<AppName>
+
+If on Windows (at least English Win XP) that should be::
+
+ C:\Documents and Settings\<User>\Application Data\Local Settings\<AppAuthor>\<AppName>
+
+or possibly::
+
+ C:\Documents and Settings\<User>\Application Data\<AppAuthor>\<AppName>
+
+for `roaming profiles <http://bit.ly/9yl3b6>`_ but that is another story.
+
+On Linux (and other Unices) the dir, according to the `XDG
+spec <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_, is::
+
+ ~/.local/share/<AppName>
+
+
+``appdirs`` to the rescue
+=========================
+
+This kind of thing is what the ``appdirs`` module is for. ``appdirs`` will
+help you choose an appropriate:
+
+- user data dir (``user_data_dir``)
+- user config dir (``user_config_dir``)
+- user cache dir (``user_cache_dir``)
+- site data dir (``site_data_dir``)
+- site config dir (``site_config_dir``)
+- user log dir (``user_log_dir``)
+
+and also:
+
+- is a single module so other Python packages can include their own private copy
+- is slightly opinionated on the directory names used. Look for "OPINION" in
+ documentation and code for when an opinion is being applied.
+
+
+some example output
+===================
+
+On Mac OS X::
+
+ >>> from appdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ '/Users/trentm/Library/Application Support/SuperApp'
+ >>> site_data_dir(appname, appauthor)
+ '/Library/Application Support/SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ '/Users/trentm/Library/Caches/SuperApp'
+ >>> user_log_dir(appname, appauthor)
+ '/Users/trentm/Library/Logs/SuperApp'
+
+On Windows 7::
+
+ >>> from appdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp'
+ >>> user_data_dir(appname, appauthor, roaming=True)
+ 'C:\\Users\\trentm\\AppData\\Roaming\\Acme\\SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Cache'
+ >>> user_log_dir(appname, appauthor)
+ 'C:\\Users\\trentm\\AppData\\Local\\Acme\\SuperApp\\Logs'
+
+On Linux::
+
+ >>> from appdirs import *
+ >>> appname = "SuperApp"
+ >>> appauthor = "Acme"
+ >>> user_data_dir(appname, appauthor)
+ '/home/trentm/.local/share/SuperApp
+ >>> site_data_dir(appname, appauthor)
+ '/usr/local/share/SuperApp'
+ >>> site_data_dir(appname, appauthor, multipath=True)
+ '/usr/local/share/SuperApp:/usr/share/SuperApp'
+ >>> user_cache_dir(appname, appauthor)
+ '/home/trentm/.cache/SuperApp'
+ >>> user_log_dir(appname, appauthor)
+ '/home/trentm/.cache/SuperApp/log'
+ >>> user_config_dir(appname)
+ '/home/trentm/.config/SuperApp'
+ >>> site_config_dir(appname)
+ '/etc/xdg/SuperApp'
+ >>> os.environ['XDG_CONFIG_DIRS'] = '/etc:/usr/local/etc'
+ >>> site_config_dir(appname, multipath=True)
+ '/etc/SuperApp:/usr/local/etc/SuperApp'
+
+
+``AppDirs`` for convenience
+===========================
+
+::
+
+ >>> from appdirs import AppDirs
+ >>> dirs = AppDirs("SuperApp", "Acme")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp'
+ >>> dirs.site_data_dir
+ '/Library/Application Support/SuperApp'
+ >>> dirs.user_cache_dir
+ '/Users/trentm/Library/Caches/SuperApp'
+ >>> dirs.user_log_dir
+ '/Users/trentm/Library/Logs/SuperApp'
+
+
+
+Per-version isolation
+=====================
+
+If you have multiple versions of your app in use that you want to be
+able to run side-by-side, then you may want version-isolation for these
+dirs::
+
+ >>> from appdirs import AppDirs
+ >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp/1.0'
+ >>> dirs.site_data_dir
+ '/Library/Application Support/SuperApp/1.0'
+ >>> dirs.user_cache_dir
+ '/Users/trentm/Library/Caches/SuperApp/1.0'
+ >>> dirs.user_log_dir
+ '/Users/trentm/Library/Logs/SuperApp/1.0'
+
+
+
+appdirs Changelog
+=================
+
+appdirs 1.4.3
+-------------
+- [PR #76] Python 3.6 invalid escape sequence deprecation fixes
+- Fix for Python 3.6 support
+
+appdirs 1.4.2
+-------------
+- [PR #84] Allow installing without setuptools
+- [PR #86] Fix string delimiters in setup.py description
+- Add Python 3.6 support
+
+appdirs 1.4.1
+-------------
+- [issue #38] Fix _winreg import on Windows Py3
+- [issue #55] Make appname optional
+
+appdirs 1.4.0
+-------------
+- [PR #42] AppAuthor is now optional on Windows
+- [issue 41] Support Jython on Windows, Mac, and Unix-like platforms. Windows
+ support requires `JNA <https://github.com/twall/jna>`_.
+- [PR #44] Fix incorrect behaviour of the site_config_dir method
+
+appdirs 1.3.0
+-------------
+- [Unix, issue 16] Conform to XDG standard, instead of breaking it for
+ everybody
+- [Unix] Removes gratuitous case mangling of the case, since \*nix-es are
+ usually case sensitive, so mangling is not wise
+- [Unix] Fixes the utterly wrong behaviour in ``site_data_dir``, return result
+ based on XDG_DATA_DIRS and make room for respecting the standard which
+ specifies XDG_DATA_DIRS is a multiple-value variable
+- [Issue 6] Add ``*_config_dir`` which are distinct on nix-es, according to
+ XDG specs; on Windows and Mac return the corresponding ``*_data_dir``
+
+appdirs 1.2.0
+-------------
+
+- [Unix] Put ``user_log_dir`` under the *cache* dir on Unix. Seems to be more
+ typical.
+- [issue 9] Make ``unicode`` work on py3k.
+
+appdirs 1.1.0
+-------------
+
+- [issue 4] Add ``AppDirs.user_log_dir``.
+- [Unix, issue 2, issue 7] appdirs now conforms to `XDG base directory spec
+ <http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html>`_.
+- [Mac, issue 5] Fix ``site_data_dir()`` on Mac.
+- [Mac] Drop use of 'Carbon' module in favour of hardcoded paths; supports
+ Python3 now.
+- [Windows] Append "Cache" to ``user_cache_dir`` on Windows by default. Use
+ ``opinion=False`` option to disable this.
+- Add ``appdirs.AppDirs`` convenience class. Usage:
+
+ >>> dirs = AppDirs("SuperApp", "Acme", version="1.0")
+ >>> dirs.user_data_dir
+ '/Users/trentm/Library/Application Support/SuperApp/1.0'
+
+- [Windows] Cherry-pick Komodo's change to downgrade paths to the Windows short
+ paths if there are high bit chars.
+- [Linux] Change default ``user_cache_dir()`` on Linux to be singular, e.g.
+ "~/.superapp/cache".
+- [Windows] Add ``roaming`` option to ``user_data_dir()`` (for use on Windows only)
+ and change the default ``user_data_dir`` behaviour to use a *non*-roaming
+ profile dir (``CSIDL_LOCAL_APPDATA`` instead of ``CSIDL_APPDATA``). Why? Because
+ a large roaming profile can cause login speed issues. The "only syncs on
+ logout" behaviour can cause surprises in appdata info.
+
+
+appdirs 1.0.1 (never released)
+------------------------------
+
+Started this changelog 27 July 2010. Before that this module originated in the
+`Komodo <http://www.activestate.com/komodo>`_ product as ``applib.py`` and then
+as `applib/location.py
+<http://github.com/ActiveState/applib/blob/master/applib/location.py>`_ (used by
+`PyPM <http://code.activestate.com/pypm/>`_ in `ActivePython
+<http://www.activestate.com/activepython>`_). This is basically a fork of
+applib.py 1.0.1 and applib/location.py 1.0.1.
+
+
+
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/RECORD b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/RECORD
new file mode 100644
index 00000000..3f45ff59
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/RECORD
@@ -0,0 +1,10 @@
+__pycache__/appdirs.cpython-310.pyc,,
+appdirs-1.4.3.dist-info/DESCRIPTION.rst,sha256=77Fe8OIOLSjDSNdLiL5xywMKO-AGE42rdXkqKo4Ee-k,7531
+appdirs-1.4.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+appdirs-1.4.3.dist-info/METADATA,sha256=3IFw6jTfImdOqsCb2GYvVR157tL7KEzfRAszn382csk,8773
+appdirs-1.4.3.dist-info/RECORD,,
+appdirs-1.4.3.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+appdirs-1.4.3.dist-info/WHEEL,sha256=o2k-Qa-RMNIJmUdIc7KU6VWR_ErNRbWNlxDIpl7lm34,110
+appdirs-1.4.3.dist-info/metadata.json,sha256=fL_Q-GuFJu3PJxMrwU7SdsI8RGqjIfi2AvouCSF5DSA,1359
+appdirs-1.4.3.dist-info/top_level.txt,sha256=nKncE8CUqZERJ6VuQWL4_bkunSPDNfn7KZqb4Tr5YEM,8
+appdirs.py,sha256=MievUEuv3l_mQISH5SF0shDk_BNhHHzYiAPrT3ITN4I,24701
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/REQUESTED b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/REQUESTED
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/WHEEL b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/WHEEL
new file mode 100644
index 00000000..8b6dd1b5
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.29.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/metadata.json b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/metadata.json
new file mode 100644
index 00000000..da1e5f3a
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.2", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: CPython", "Topic :: Software Development :: Libraries :: Python Modules"], "extensions": {"python.details": {"contacts": [{"email": "trentm@gmail.com; github@srid.name; jr@its.to", "name": "Trent Mick; Sridhar Ratnakumar; Jeff Rouse", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "http://github.com/ActiveState/appdirs"}}}, "generator": "bdist_wheel (0.29.0)", "keywords": ["application", "directory", "log", "cache", "user"], "license": "MIT", "metadata_version": "2.0", "name": "appdirs", "summary": "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\".", "test_requires": [{"requires": []}], "version": "1.4.3"} \ No newline at end of file
diff --git a/pkg_resources/_vendor/appdirs-1.4.3.dist-info/top_level.txt b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/top_level.txt
new file mode 100644
index 00000000..d64bc321
--- /dev/null
+++ b/pkg_resources/_vendor/appdirs-1.4.3.dist-info/top_level.txt
@@ -0,0 +1 @@
+appdirs
diff --git a/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/LICENSE b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/LICENSE
new file mode 100644
index 00000000..378b991a
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2017-2019 Brett Cannon, Barry Warsaw
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/METADATA b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/METADATA
new file mode 100644
index 00000000..cdb1e783
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/METADATA
@@ -0,0 +1,86 @@
+Metadata-Version: 2.1
+Name: importlib-resources
+Version: 5.4.0
+Summary: Read resources from Python packages
+Home-page: https://github.com/python/importlib_resources
+Author: Barry Warsaw
+Author-email: barry@python.org
+License: UNKNOWN
+Project-URL: Documentation, https://importlib-resources.readthedocs.io/
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.6
+License-File: LICENSE
+Requires-Dist: zipp (>=3.1.0) ; python_version < "3.10"
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/importlib_resources.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/importlib_resources.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/importlib_resources
+
+.. image:: https://github.com/python/importlib_resources/workflows/tests/badge.svg
+ :target: https://github.com/python/importlib_resources/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/importlib-resources/badge/?version=latest
+ :target: https://importlib-resources.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+``importlib_resources`` is a backport of Python standard library
+`importlib.resources
+<https://docs.python.org/3/library/importlib.html#module-importlib.resources>`_
+module for older Pythons.
+
+The key goal of this module is to replace parts of `pkg_resources
+<https://setuptools.readthedocs.io/en/latest/pkg_resources.html>`_ with a
+solution in Python's stdlib that relies on well-defined APIs. This makes
+reading resources included in packages easier, with more stable and consistent
+semantics.
+
+Compatibility
+=============
+
+New features are introduced in this third-party library and later merged
+into CPython. The following table indicates which versions of this library
+were contributed to different versions in the standard library:
+
+.. list-table::
+ :header-rows: 1
+
+ * - importlib_resources
+ - stdlib
+ * - 5.2
+ - 3.11
+ * - 5.0
+ - 3.10
+ * - 1.3
+ - 3.9
+ * - 0.5 (?)
+ - 3.7
+
+
diff --git a/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/RECORD b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/RECORD
new file mode 100644
index 00000000..7a68a2f2
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/RECORD
@@ -0,0 +1,75 @@
+importlib_resources-5.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+importlib_resources-5.4.0.dist-info/LICENSE,sha256=uWRjFdYGataJX2ziXk048ItUglQmjng3GWBALaWA36U,568
+importlib_resources-5.4.0.dist-info/METADATA,sha256=i5jH25IbM0Ls6u6UzSSCOa0c8hpDvePxqgnQwh2T5Io,3135
+importlib_resources-5.4.0.dist-info/RECORD,,
+importlib_resources-5.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources-5.4.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+importlib_resources-5.4.0.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20
+importlib_resources/__init__.py,sha256=zuA0lbRgtVVCcAztM0z5LuBiOCV9L_3qtI6mW2p5xAg,525
+importlib_resources/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/__pycache__/_adapters.cpython-310.pyc,,
+importlib_resources/__pycache__/_common.cpython-310.pyc,,
+importlib_resources/__pycache__/_compat.cpython-310.pyc,,
+importlib_resources/__pycache__/_itertools.cpython-310.pyc,,
+importlib_resources/__pycache__/_legacy.cpython-310.pyc,,
+importlib_resources/__pycache__/abc.cpython-310.pyc,,
+importlib_resources/__pycache__/readers.cpython-310.pyc,,
+importlib_resources/__pycache__/simple.cpython-310.pyc,,
+importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504
+importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741
+importlib_resources/_compat.py,sha256=3LpkIfeN9x4oXjRea5TxZP5VYhPlzuVRhGe-hEv-S0s,2704
+importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884
+importlib_resources/_legacy.py,sha256=TMLkx6aEM6U8xIREPXqGZrMbUhTiPUuPl6ESD7RdYj4,3494
+importlib_resources/abc.py,sha256=MvTJJXajbl74s36Gyeesf76egtbFnh-TMtzQMVhFWXo,3886
+importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566
+importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836
+importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/_compat.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_contents.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_files.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_open.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_path.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_read.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_reader.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_resource.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/update-zips.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/util.cpython-310.pyc,,
+importlib_resources/tests/_compat.py,sha256=QGI_4p0DXybypoYvw0kr3jfQqvls3p8u4wy4Wvf0Z_o,435
+importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/one/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13
+importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/two/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13
+importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/namespacedata01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/test_compatibilty_files.py,sha256=NWkbIsylI8Wz3Dwsxo1quT4ZI6ToXFA2mojCG6Dzuxw,3260
+importlib_resources/tests/test_contents.py,sha256=V1Xfk3lqTDdvUsZuV18Kndf0CT_tkM2oEIwk9Vv0rhg,968
+importlib_resources/tests/test_files.py,sha256=1Nqv6VM_MjfwrmtXYL1a1CMT0QhCxi3hNMqwXlfMQTg,1184
+importlib_resources/tests/test_open.py,sha256=pmEgdrSFdM83L6FxtR8U_RT9BfI3JZ4snGmM_ZZIegY,2565
+importlib_resources/tests/test_path.py,sha256=xvPteNA-UKavDhKgLgrQuXSxKWYH7Q4nSNDVfBX95Gs,2103
+importlib_resources/tests/test_read.py,sha256=EyYvpHJ_7F4LuX2EU_c5EerIBQfRhOFmiIR7LOc5Y5E,2408
+importlib_resources/tests/test_reader.py,sha256=hgXHquqAEnioemv20ZZcDlVaiOrcZKADO37_FkiQ00Y,4286
+importlib_resources/tests/test_resource.py,sha256=DqfLNc9kaN5obqxU8kn0sRUWMf9MygagrpfMV5-QfWg,8145
+importlib_resources/tests/update-zips.py,sha256=x3iJVqWnMM5qp4Oob2Pl3o6Yi03sUjEv_5Wf-UCg3ps,1415
+importlib_resources/tests/util.py,sha256=X1j-0C96pu3_tmtJuLhzfBfcfMenOphDLkxtCt5j7t4,5309
+importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876
+importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698
diff --git a/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED
diff --git a/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/WHEEL b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt
new file mode 100644
index 00000000..58ad1bd3
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+importlib_resources
diff --git a/pkg_resources/_vendor/importlib_resources/__init__.py b/pkg_resources/_vendor/importlib_resources/__init__.py
new file mode 100644
index 00000000..34e3a995
--- /dev/null
+++ b/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/pkg_resources/_vendor/importlib_resources/_adapters.py b/pkg_resources/_vendor/importlib_resources/_adapters.py
new file mode 100644
index 00000000..ea363d86
--- /dev/null
+++ b/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/pkg_resources/_vendor/importlib_resources/_common.py b/pkg_resources/_vendor/importlib_resources/_common.py
new file mode 100644
index 00000000..a12e2c75
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/_common.py
@@ -0,0 +1,104 @@
+import os
+import pathlib
+import tempfile
+import functools
+import contextlib
+import types
+import importlib
+
+from typing import Union, Optional
+from .abc import ResourceReader, Traversable
+
+from ._compat import wrap_spec
+
+Package = Union[types.ModuleType, str]
+
+
+def files(package):
+ # type: (Package) -> Traversable
+ """
+ Get a Traversable resource from a package
+ """
+ return from_package(get_package(package))
+
+
+def get_resource_reader(package):
+ # type: (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
+
+
+def resolve(cand):
+ # type: (Package) -> types.ModuleType
+ return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand)
+
+
+def get_package(package):
+ # type: (Package) -> types.ModuleType
+ """Take a package name or module object and return the module.
+
+ Raise an exception if the resolved module is not a package.
+ """
+ resolved = resolve(package)
+ if wrap_spec(resolved).submodule_search_locations is None:
+ raise TypeError(f'{package!r} is not a package')
+ return resolved
+
+
+def from_package(package):
+ """
+ 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=''):
+ # 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
+
+
+@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 _tempfile(path.read_bytes, suffix=path.name)
+
+
+@as_file.register(pathlib.Path)
+@contextlib.contextmanager
+def _(path):
+ """
+ Degenerate behavior for pathlib.Path objects.
+ """
+ yield path
diff --git a/pkg_resources/_vendor/importlib_resources/_compat.py b/pkg_resources/_vendor/importlib_resources/_compat.py
new file mode 100644
index 00000000..cb9fc820
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/_compat.py
@@ -0,0 +1,98 @@
+# flake8: noqa
+
+import abc
+import sys
+import pathlib
+from contextlib import suppress
+
+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)
diff --git a/pkg_resources/_vendor/importlib_resources/_itertools.py b/pkg_resources/_vendor/importlib_resources/_itertools.py
new file mode 100644
index 00000000..cce05582
--- /dev/null
+++ b/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/pkg_resources/_vendor/importlib_resources/_legacy.py b/pkg_resources/_vendor/importlib_resources/_legacy.py
new file mode 100644
index 00000000..1d5d3f1f
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/_legacy.py
@@ -0,0 +1,121 @@
+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):
+ # type: (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/pkg_resources/_vendor/importlib_resources/abc.py b/pkg_resources/_vendor/importlib_resources/abc.py
new file mode 100644
index 00000000..d39dc1ad
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/abc.py
@@ -0,0 +1,137 @@
+import abc
+from typing import BinaryIO, Iterable, Text
+
+from ._compat import runtime_checkable, Protocol
+
+
+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
+
+
+@runtime_checkable
+class Traversable(Protocol):
+ """
+ An object with a subset of pathlib.Path methods suitable for
+ traversing directories and opening files.
+ """
+
+ @abc.abstractmethod
+ def iterdir(self):
+ """
+ Yield Traversable objects in self
+ """
+
+ def read_bytes(self):
+ """
+ Read contents of self as bytes
+ """
+ with self.open('rb') as strm:
+ return strm.read()
+
+ def read_text(self, encoding=None):
+ """
+ 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
+ """
+
+ @abc.abstractmethod
+ def joinpath(self, child):
+ """
+ Return Traversable child in self
+ """
+
+ def __truediv__(self, child):
+ """
+ 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.
+ """
+
+ @abc.abstractproperty
+ 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):
+ """Return a Traversable object for the loaded package."""
+
+ def open_resource(self, resource):
+ return self.files().joinpath(resource).open('rb')
+
+ def resource_path(self, resource):
+ raise FileNotFoundError(resource)
+
+ def is_resource(self, path):
+ return self.files().joinpath(path).is_file()
+
+ def contents(self):
+ return (item.name for item in self.files().iterdir())
diff --git a/pkg_resources/_vendor/importlib_resources/py.typed b/pkg_resources/_vendor/importlib_resources/py.typed
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/py.typed
diff --git a/pkg_resources/_vendor/importlib_resources/readers.py b/pkg_resources/_vendor/importlib_resources/readers.py
new file mode 100644
index 00000000..f1190ca4
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/readers.py
@@ -0,0 +1,122 @@
+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, child):
+ # first try to find child in current paths
+ for file in self.iterdir():
+ if file.name == child:
+ return file
+ # if it does not exist, construct it with the first path
+ return self._paths[0] / child
+
+ __truediv__ = joinpath
+
+ 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/pkg_resources/_vendor/importlib_resources/simple.py b/pkg_resources/_vendor/importlib_resources/simple.py
new file mode 100644
index 00000000..da073cbd
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/simple.py
@@ -0,0 +1,116 @@
+"""
+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.
+ """
+
+ @abc.abstractproperty
+ def package(self):
+ # type: () -> str
+ """
+ The name of the package for which this reader loads resources.
+ """
+
+ @abc.abstractmethod
+ def children(self):
+ # type: () -> List['SimpleReader']
+ """
+ Obtain an iterable of SimpleReader for available
+ child containers (e.g. directories).
+ """
+
+ @abc.abstractmethod
+ def resources(self):
+ # type: () -> List[str]
+ """
+ Obtain available named resources for this virtual package.
+ """
+
+ @abc.abstractmethod
+ def open_binary(self, resource):
+ # type: (str) -> BinaryIO
+ """
+ Obtain a File-like for a named resource.
+ """
+
+ @property
+ def name(self):
+ return self.package.split('.')[-1]
+
+
+class ResourceHandle(Traversable):
+ """
+ Handle to a named resource in a ResourceReader.
+ """
+
+ def __init__(self, parent, name):
+ # type: (ResourceContainer, str) -> None
+ 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 ResourceContainer(Traversable):
+ """
+ Traversable container for a package's resources via its reader.
+ """
+
+ def __init__(self, reader):
+ # type: (SimpleReader) -> None
+ 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()
+
+ def joinpath(self, name):
+ return next(
+ traversable for traversable in self.iterdir() if traversable.name == name
+ )
+
+
+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/pkg_resources/_vendor/importlib_resources/tests/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/_compat.py b/pkg_resources/_vendor/importlib_resources/tests/_compat.py
new file mode 100644
index 00000000..4c99cffd
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/_compat.py
@@ -0,0 +1,19 @@
+import os
+
+
+try:
+ from test.support import import_helper # type: ignore
+except ImportError:
+ # Python 3.9 and earlier
+ class import_helper: # type: ignore
+ from test.support import modules_setup, modules_cleanup
+
+
+try:
+ # Python 3.10
+ from test.support.os_helper import unlink
+except ImportError:
+ from test.support import unlink as _unlink
+
+ def unlink(target):
+ return _unlink(os.fspath(target))
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data01/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/data01/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data01/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data01/binary.file b/pkg_resources/_vendor/importlib_resources/tests/data01/binary.file
new file mode 100644
index 00000000..eaf36c1d
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data01/binary.file
Binary files differ
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/binary.file b/pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/binary.file
new file mode 100644
index 00000000..eaf36c1d
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data01/subdirectory/binary.file
Binary files differ
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data01/utf-16.file b/pkg_resources/_vendor/importlib_resources/tests/data01/utf-16.file
new file mode 100644
index 00000000..2cb77229
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data01/utf-16.file
Binary files differ
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data01/utf-8.file b/pkg_resources/_vendor/importlib_resources/tests/data01/utf-8.file
new file mode 100644
index 00000000..1c0132ad
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data01/utf-8.file
@@ -0,0 +1 @@
+Hello, UTF-8 world!
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data02/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/data02/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data02/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data02/one/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/data02/one/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data02/one/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data02/one/resource1.txt b/pkg_resources/_vendor/importlib_resources/tests/data02/one/resource1.txt
new file mode 100644
index 00000000..61a813e4
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data02/one/resource1.txt
@@ -0,0 +1 @@
+one resource
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data02/two/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/data02/two/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data02/two/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/data02/two/resource2.txt b/pkg_resources/_vendor/importlib_resources/tests/data02/two/resource2.txt
new file mode 100644
index 00000000..a80ce46e
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/data02/two/resource2.txt
@@ -0,0 +1 @@
+two resource
diff --git a/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/binary.file b/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/binary.file
new file mode 100644
index 00000000..eaf36c1d
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/binary.file
Binary files differ
diff --git a/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-16.file b/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-16.file
new file mode 100644
index 00000000..2cb77229
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-16.file
Binary files differ
diff --git a/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-8.file b/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-8.file
new file mode 100644
index 00000000..1c0132ad
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/namespacedata01/utf-8.file
@@ -0,0 +1 @@
+Hello, UTF-8 world!
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_compatibilty_files.py b/pkg_resources/_vendor/importlib_resources/tests/test_compatibilty_files.py
new file mode 100644
index 00000000..d92c7c56
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_compatibilty_files.py
@@ -0,0 +1,102 @@
+import io
+import unittest
+
+import importlib_resources as resources
+
+from importlib_resources._adapters import (
+ CompatibilityFiles,
+ wrap_spec,
+)
+
+from . import util
+
+
+class CompatibilityFilesTests(unittest.TestCase):
+ @property
+ def package(self):
+ bytes_data = io.BytesIO(b'Hello, world!')
+ return util.create_package(
+ file=bytes_data,
+ path='some_path',
+ contents=('a', 'b', 'c'),
+ )
+
+ @property
+ def files(self):
+ return resources.files(self.package)
+
+ def test_spec_path_iter(self):
+ self.assertEqual(
+ sorted(path.name for path in self.files.iterdir()),
+ ['a', 'b', 'c'],
+ )
+
+ def test_child_path_iter(self):
+ self.assertEqual(list((self.files / 'a').iterdir()), [])
+
+ def test_orphan_path_iter(self):
+ self.assertEqual(list((self.files / 'a' / 'a').iterdir()), [])
+ self.assertEqual(list((self.files / 'a' / 'a' / 'a').iterdir()), [])
+
+ def test_spec_path_is(self):
+ self.assertFalse(self.files.is_file())
+ self.assertFalse(self.files.is_dir())
+
+ def test_child_path_is(self):
+ self.assertTrue((self.files / 'a').is_file())
+ self.assertFalse((self.files / 'a').is_dir())
+
+ def test_orphan_path_is(self):
+ self.assertFalse((self.files / 'a' / 'a').is_file())
+ self.assertFalse((self.files / 'a' / 'a').is_dir())
+ self.assertFalse((self.files / 'a' / 'a' / 'a').is_file())
+ self.assertFalse((self.files / 'a' / 'a' / 'a').is_dir())
+
+ def test_spec_path_name(self):
+ self.assertEqual(self.files.name, 'testingpackage')
+
+ def test_child_path_name(self):
+ self.assertEqual((self.files / 'a').name, 'a')
+
+ def test_orphan_path_name(self):
+ self.assertEqual((self.files / 'a' / 'b').name, 'b')
+ self.assertEqual((self.files / 'a' / 'b' / 'c').name, 'c')
+
+ def test_spec_path_open(self):
+ self.assertEqual(self.files.read_bytes(), b'Hello, world!')
+ self.assertEqual(self.files.read_text(), 'Hello, world!')
+
+ def test_child_path_open(self):
+ self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!')
+ self.assertEqual((self.files / 'a').read_text(), 'Hello, world!')
+
+ def test_orphan_path_open(self):
+ with self.assertRaises(FileNotFoundError):
+ (self.files / 'a' / 'b').read_bytes()
+ with self.assertRaises(FileNotFoundError):
+ (self.files / 'a' / 'b' / 'c').read_bytes()
+
+ def test_open_invalid_mode(self):
+ with self.assertRaises(ValueError):
+ self.files.open('0')
+
+ def test_orphan_path_invalid(self):
+ with self.assertRaises(ValueError):
+ CompatibilityFiles.OrphanPath()
+
+ def test_wrap_spec(self):
+ spec = wrap_spec(self.package)
+ self.assertIsInstance(spec.loader.get_resource_reader(None), CompatibilityFiles)
+
+
+class CompatibilityFilesNoReaderTests(unittest.TestCase):
+ @property
+ def package(self):
+ return util.create_package_from_loader(None)
+
+ @property
+ def files(self):
+ return resources.files(self.package)
+
+ def test_spec_path_joinpath(self):
+ self.assertIsInstance(self.files / 'a', CompatibilityFiles.OrphanPath)
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_contents.py b/pkg_resources/_vendor/importlib_resources/tests/test_contents.py
new file mode 100644
index 00000000..525568e8
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_contents.py
@@ -0,0 +1,43 @@
+import unittest
+import importlib_resources as resources
+
+from . import data01
+from . import util
+
+
+class ContentsTests:
+ expected = {
+ '__init__.py',
+ 'binary.file',
+ 'subdirectory',
+ 'utf-16.file',
+ 'utf-8.file',
+ }
+
+ def test_contents(self):
+ contents = {path.name for path in resources.files(self.data).iterdir()}
+ assert self.expected <= contents
+
+
+class ContentsDiskTests(ContentsTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
+ expected = {
+ # no __init__ because of namespace design
+ # no subdirectory as incidental difference in fixture
+ 'binary.file',
+ 'utf-16.file',
+ 'utf-8.file',
+ }
+
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_files.py b/pkg_resources/_vendor/importlib_resources/tests/test_files.py
new file mode 100644
index 00000000..2676b49e
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_files.py
@@ -0,0 +1,46 @@
+import typing
+import unittest
+
+import importlib_resources as resources
+from importlib_resources.abc import Traversable
+from . import data01
+from . import util
+
+
+class FilesTests:
+ def test_read_bytes(self):
+ files = resources.files(self.data)
+ actual = files.joinpath('utf-8.file').read_bytes()
+ assert actual == b'Hello, UTF-8 world!\n'
+
+ def test_read_text(self):
+ files = resources.files(self.data)
+ actual = files.joinpath('utf-8.file').read_text(encoding='utf-8')
+ assert actual == 'Hello, UTF-8 world!\n'
+
+ @unittest.skipUnless(
+ hasattr(typing, 'runtime_checkable'),
+ "Only suitable when typing supports runtime_checkable",
+ )
+ def test_traversable(self):
+ assert isinstance(resources.files(self.data), Traversable)
+
+
+class OpenDiskTests(FilesTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+class OpenNamespaceTests(FilesTests, unittest.TestCase):
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_open.py b/pkg_resources/_vendor/importlib_resources/tests/test_open.py
new file mode 100644
index 00000000..87b42c3d
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_open.py
@@ -0,0 +1,81 @@
+import unittest
+
+import importlib_resources as resources
+from . import data01
+from . import util
+
+
+class CommonBinaryTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ target = resources.files(package).joinpath(path)
+ with target.open('rb'):
+ pass
+
+
+class CommonTextTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ target = resources.files(package).joinpath(path)
+ with target.open():
+ pass
+
+
+class OpenTests:
+ def test_open_binary(self):
+ target = resources.files(self.data) / 'binary.file'
+ with target.open('rb') as fp:
+ result = fp.read()
+ self.assertEqual(result, b'\x00\x01\x02\x03')
+
+ def test_open_text_default_encoding(self):
+ target = resources.files(self.data) / 'utf-8.file'
+ with target.open() as fp:
+ result = fp.read()
+ self.assertEqual(result, 'Hello, UTF-8 world!\n')
+
+ def test_open_text_given_encoding(self):
+ target = resources.files(self.data) / 'utf-16.file'
+ with target.open(encoding='utf-16', errors='strict') as fp:
+ result = fp.read()
+ self.assertEqual(result, 'Hello, UTF-16 world!\n')
+
+ def test_open_text_with_errors(self):
+ # Raises UnicodeError without the 'errors' argument.
+ target = resources.files(self.data) / 'utf-16.file'
+ with target.open(encoding='utf-8', errors='strict') as fp:
+ self.assertRaises(UnicodeError, fp.read)
+ with target.open(encoding='utf-8', errors='ignore') as fp:
+ result = fp.read()
+ self.assertEqual(
+ result,
+ 'H\x00e\x00l\x00l\x00o\x00,\x00 '
+ '\x00U\x00T\x00F\x00-\x001\x006\x00 '
+ '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00',
+ )
+
+ def test_open_binary_FileNotFoundError(self):
+ target = resources.files(self.data) / 'does-not-exist'
+ self.assertRaises(FileNotFoundError, target.open, 'rb')
+
+ def test_open_text_FileNotFoundError(self):
+ target = resources.files(self.data) / 'does-not-exist'
+ self.assertRaises(FileNotFoundError, target.open)
+
+
+class OpenDiskTests(OpenTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class OpenDiskNamespaceTests(OpenTests, unittest.TestCase):
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
+
+
+class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_path.py b/pkg_resources/_vendor/importlib_resources/tests/test_path.py
new file mode 100644
index 00000000..4f4d3943
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_path.py
@@ -0,0 +1,64 @@
+import io
+import unittest
+
+import importlib_resources as resources
+from . import data01
+from . import util
+
+
+class CommonTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ with resources.as_file(resources.files(package).joinpath(path)):
+ pass
+
+
+class PathTests:
+ def test_reading(self):
+ # Path should be readable.
+ # Test also implicitly verifies the returned object is a pathlib.Path
+ # instance.
+ target = resources.files(self.data) / 'utf-8.file'
+ with resources.as_file(target) as path:
+ self.assertTrue(path.name.endswith("utf-8.file"), repr(path))
+ # pathlib.Path.read_text() was introduced in Python 3.5.
+ with path.open('r', encoding='utf-8') as file:
+ text = file.read()
+ self.assertEqual('Hello, UTF-8 world!\n', text)
+
+
+class PathDiskTests(PathTests, unittest.TestCase):
+ data = data01
+
+ def test_natural_path(self):
+ """
+ Guarantee the internal implementation detail that
+ file-system-backed resources do not get the tempdir
+ treatment.
+ """
+ target = resources.files(self.data) / 'utf-8.file'
+ with resources.as_file(target) as path:
+ assert 'data' in str(path)
+
+
+class PathMemoryTests(PathTests, unittest.TestCase):
+ def setUp(self):
+ file = io.BytesIO(b'Hello, UTF-8 world!\n')
+ self.addCleanup(file.close)
+ self.data = util.create_package(
+ file=file, path=FileNotFoundError("package exists only in memory")
+ )
+ self.data.__spec__.origin = None
+ self.data.__spec__.has_location = False
+
+
+class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase):
+ def test_remove_in_context_manager(self):
+ # It is not an error if the file that was temporarily stashed on the
+ # file system is removed inside the `with` stanza.
+ target = resources.files(self.data) / 'utf-8.file'
+ with resources.as_file(target) as path:
+ path.unlink()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_read.py b/pkg_resources/_vendor/importlib_resources/tests/test_read.py
new file mode 100644
index 00000000..41dd6db5
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_read.py
@@ -0,0 +1,76 @@
+import unittest
+import importlib_resources as resources
+
+from . import data01
+from . import util
+from importlib import import_module
+
+
+class CommonBinaryTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ resources.files(package).joinpath(path).read_bytes()
+
+
+class CommonTextTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ resources.files(package).joinpath(path).read_text()
+
+
+class ReadTests:
+ def test_read_bytes(self):
+ result = resources.files(self.data).joinpath('binary.file').read_bytes()
+ self.assertEqual(result, b'\0\1\2\3')
+
+ def test_read_text_default_encoding(self):
+ result = resources.files(self.data).joinpath('utf-8.file').read_text()
+ self.assertEqual(result, 'Hello, UTF-8 world!\n')
+
+ def test_read_text_given_encoding(self):
+ result = (
+ resources.files(self.data)
+ .joinpath('utf-16.file')
+ .read_text(encoding='utf-16')
+ )
+ self.assertEqual(result, 'Hello, UTF-16 world!\n')
+
+ def test_read_text_with_errors(self):
+ # Raises UnicodeError without the 'errors' argument.
+ target = resources.files(self.data) / 'utf-16.file'
+ self.assertRaises(UnicodeError, target.read_text, encoding='utf-8')
+ result = target.read_text(encoding='utf-8', errors='ignore')
+ self.assertEqual(
+ result,
+ 'H\x00e\x00l\x00l\x00o\x00,\x00 '
+ '\x00U\x00T\x00F\x00-\x001\x006\x00 '
+ '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00',
+ )
+
+
+class ReadDiskTests(ReadTests, unittest.TestCase):
+ data = data01
+
+
+class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
+ def test_read_submodule_resource(self):
+ submodule = import_module('ziptestdata.subdirectory')
+ result = resources.files(submodule).joinpath('binary.file').read_bytes()
+ self.assertEqual(result, b'\0\1\2\3')
+
+ def test_read_submodule_resource_by_name(self):
+ result = (
+ resources.files('ziptestdata.subdirectory')
+ .joinpath('binary.file')
+ .read_bytes()
+ )
+ self.assertEqual(result, b'\0\1\2\3')
+
+
+class ReadNamespaceTests(ReadTests, unittest.TestCase):
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_reader.py b/pkg_resources/_vendor/importlib_resources/tests/test_reader.py
new file mode 100644
index 00000000..16841a50
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_reader.py
@@ -0,0 +1,128 @@
+import os.path
+import sys
+import pathlib
+import unittest
+
+from importlib import import_module
+from importlib_resources.readers import MultiplexedPath, NamespaceReader
+
+
+class MultiplexedPathTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ path = pathlib.Path(__file__).parent / 'namespacedata01'
+ cls.folder = str(path)
+
+ def test_init_no_paths(self):
+ with self.assertRaises(FileNotFoundError):
+ MultiplexedPath()
+
+ def test_init_file(self):
+ with self.assertRaises(NotADirectoryError):
+ MultiplexedPath(os.path.join(self.folder, 'binary.file'))
+
+ def test_iterdir(self):
+ contents = {path.name for path in MultiplexedPath(self.folder).iterdir()}
+ try:
+ contents.remove('__pycache__')
+ except (KeyError, ValueError):
+ pass
+ self.assertEqual(contents, {'binary.file', 'utf-16.file', 'utf-8.file'})
+
+ def test_iterdir_duplicate(self):
+ data01 = os.path.abspath(os.path.join(__file__, '..', 'data01'))
+ contents = {
+ path.name for path in MultiplexedPath(self.folder, data01).iterdir()
+ }
+ for remove in ('__pycache__', '__init__.pyc'):
+ try:
+ contents.remove(remove)
+ except (KeyError, ValueError):
+ pass
+ self.assertEqual(
+ contents,
+ {'__init__.py', 'binary.file', 'subdirectory', 'utf-16.file', 'utf-8.file'},
+ )
+
+ def test_is_dir(self):
+ self.assertEqual(MultiplexedPath(self.folder).is_dir(), True)
+
+ def test_is_file(self):
+ self.assertEqual(MultiplexedPath(self.folder).is_file(), False)
+
+ def test_open_file(self):
+ path = MultiplexedPath(self.folder)
+ with self.assertRaises(FileNotFoundError):
+ path.read_bytes()
+ with self.assertRaises(FileNotFoundError):
+ path.read_text()
+ with self.assertRaises(FileNotFoundError):
+ path.open()
+
+ def test_join_path(self):
+ prefix = os.path.abspath(os.path.join(__file__, '..'))
+ data01 = os.path.join(prefix, 'data01')
+ path = MultiplexedPath(self.folder, data01)
+ self.assertEqual(
+ str(path.joinpath('binary.file'))[len(prefix) + 1 :],
+ os.path.join('namespacedata01', 'binary.file'),
+ )
+ self.assertEqual(
+ str(path.joinpath('subdirectory'))[len(prefix) + 1 :],
+ os.path.join('data01', 'subdirectory'),
+ )
+ self.assertEqual(
+ str(path.joinpath('imaginary'))[len(prefix) + 1 :],
+ os.path.join('namespacedata01', 'imaginary'),
+ )
+
+ def test_repr(self):
+ self.assertEqual(
+ repr(MultiplexedPath(self.folder)),
+ f"MultiplexedPath('{self.folder}')",
+ )
+
+ def test_name(self):
+ self.assertEqual(
+ MultiplexedPath(self.folder).name,
+ os.path.basename(self.folder),
+ )
+
+
+class NamespaceReaderTest(unittest.TestCase):
+ site_dir = str(pathlib.Path(__file__).parent)
+
+ @classmethod
+ def setUpClass(cls):
+ sys.path.append(cls.site_dir)
+
+ @classmethod
+ def tearDownClass(cls):
+ sys.path.remove(cls.site_dir)
+
+ def test_init_error(self):
+ with self.assertRaises(ValueError):
+ NamespaceReader(['path1', 'path2'])
+
+ def test_resource_path(self):
+ namespacedata01 = import_module('namespacedata01')
+ reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
+
+ root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
+ self.assertEqual(
+ reader.resource_path('binary.file'), os.path.join(root, 'binary.file')
+ )
+ self.assertEqual(
+ reader.resource_path('imaginary'), os.path.join(root, 'imaginary')
+ )
+
+ def test_files(self):
+ namespacedata01 = import_module('namespacedata01')
+ reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
+ root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
+ self.assertIsInstance(reader.files(), MultiplexedPath)
+ self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pkg_resources/_vendor/importlib_resources/tests/test_resource.py b/pkg_resources/_vendor/importlib_resources/tests/test_resource.py
new file mode 100644
index 00000000..5affd8b0
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/test_resource.py
@@ -0,0 +1,252 @@
+import sys
+import unittest
+import importlib_resources as resources
+import uuid
+import pathlib
+
+from . import data01
+from . import zipdata01, zipdata02
+from . import util
+from importlib import import_module
+from ._compat import import_helper, unlink
+
+
+class ResourceTests:
+ # Subclasses are expected to set the `data` attribute.
+
+ def test_is_file_exists(self):
+ target = resources.files(self.data) / 'binary.file'
+ self.assertTrue(target.is_file())
+
+ def test_is_file_missing(self):
+ target = resources.files(self.data) / 'not-a-file'
+ self.assertFalse(target.is_file())
+
+ def test_is_dir(self):
+ target = resources.files(self.data) / 'subdirectory'
+ self.assertFalse(target.is_file())
+ self.assertTrue(target.is_dir())
+
+
+class ResourceDiskTests(ResourceTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+def names(traversable):
+ return {item.name for item in traversable.iterdir()}
+
+
+class ResourceLoaderTests(unittest.TestCase):
+ def test_resource_contents(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C']
+ )
+ self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'})
+
+ def test_is_file(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ )
+ self.assertTrue(resources.files(package).joinpath('B').is_file())
+
+ def test_is_dir(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ )
+ self.assertTrue(resources.files(package).joinpath('D').is_dir())
+
+ def test_resource_missing(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ )
+ self.assertFalse(resources.files(package).joinpath('Z').is_file())
+
+
+class ResourceCornerCaseTests(unittest.TestCase):
+ def test_package_has_no_reader_fallback(self):
+ # Test odd ball packages which:
+ # 1. Do not have a ResourceReader as a loader
+ # 2. Are not on the file system
+ # 3. Are not in a zip file
+ module = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C']
+ )
+ # Give the module a dummy loader.
+ module.__loader__ = object()
+ # Give the module a dummy origin.
+ module.__file__ = '/path/which/shall/not/be/named'
+ module.__spec__.loader = module.__loader__
+ module.__spec__.origin = module.__file__
+ self.assertFalse(resources.files(module).joinpath('A').is_file())
+
+
+class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase):
+ ZIP_MODULE = zipdata01 # type: ignore
+
+ def test_is_submodule_resource(self):
+ submodule = import_module('ziptestdata.subdirectory')
+ self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file())
+
+ def test_read_submodule_resource_by_name(self):
+ self.assertTrue(
+ resources.files('ziptestdata.subdirectory')
+ .joinpath('binary.file')
+ .is_file()
+ )
+
+ def test_submodule_contents(self):
+ submodule = import_module('ziptestdata.subdirectory')
+ self.assertEqual(
+ names(resources.files(submodule)), {'__init__.py', 'binary.file'}
+ )
+
+ def test_submodule_contents_by_name(self):
+ self.assertEqual(
+ names(resources.files('ziptestdata.subdirectory')),
+ {'__init__.py', 'binary.file'},
+ )
+
+
+class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
+ ZIP_MODULE = zipdata02 # type: ignore
+
+ def test_unrelated_contents(self):
+ """
+ Test thata zip with two unrelated subpackages return
+ distinct resources. Ref python/importlib_resources#44.
+ """
+ self.assertEqual(
+ names(resources.files('ziptestdata.one')),
+ {'__init__.py', 'resource1.txt'},
+ )
+ self.assertEqual(
+ names(resources.files('ziptestdata.two')),
+ {'__init__.py', 'resource2.txt'},
+ )
+
+
+class DeletingZipsTest(unittest.TestCase):
+ """Having accessed resources in a zip file should not keep an open
+ reference to the zip.
+ """
+
+ ZIP_MODULE = zipdata01
+
+ def setUp(self):
+ modules = import_helper.modules_setup()
+ self.addCleanup(import_helper.modules_cleanup, *modules)
+
+ data_path = pathlib.Path(self.ZIP_MODULE.__file__)
+ data_dir = data_path.parent
+ self.source_zip_path = data_dir / 'ziptestdata.zip'
+ self.zip_path = pathlib.Path(f'{uuid.uuid4()}.zip').absolute()
+ self.zip_path.write_bytes(self.source_zip_path.read_bytes())
+ sys.path.append(str(self.zip_path))
+ self.data = import_module('ziptestdata')
+
+ def tearDown(self):
+ try:
+ sys.path.remove(str(self.zip_path))
+ except ValueError:
+ pass
+
+ try:
+ del sys.path_importer_cache[str(self.zip_path)]
+ del sys.modules[self.data.__name__]
+ except KeyError:
+ pass
+
+ try:
+ unlink(self.zip_path)
+ except OSError:
+ # If the test fails, this will probably fail too
+ pass
+
+ def test_iterdir_does_not_keep_open(self):
+ c = [item.name for item in resources.files('ziptestdata').iterdir()]
+ self.zip_path.unlink()
+ del c
+
+ def test_is_file_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('binary.file').is_file()
+ self.zip_path.unlink()
+ del c
+
+ def test_is_file_failure_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('not-present').is_file()
+ self.zip_path.unlink()
+ del c
+
+ @unittest.skip("Desired but not supported.")
+ def test_as_file_does_not_keep_open(self): # pragma: no cover
+ c = resources.as_file(resources.files('ziptestdata') / 'binary.file')
+ self.zip_path.unlink()
+ del c
+
+ def test_entered_path_does_not_keep_open(self):
+ # This is what certifi does on import to make its bundle
+ # available for the process duration.
+ c = resources.as_file(
+ resources.files('ziptestdata') / 'binary.file'
+ ).__enter__()
+ self.zip_path.unlink()
+ del c
+
+ def test_read_binary_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('binary.file').read_bytes()
+ self.zip_path.unlink()
+ del c
+
+ def test_read_text_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('utf-8.file').read_text()
+ self.zip_path.unlink()
+ del c
+
+
+class ResourceFromNamespaceTest01(unittest.TestCase):
+ site_dir = str(pathlib.Path(__file__).parent)
+
+ @classmethod
+ def setUpClass(cls):
+ sys.path.append(cls.site_dir)
+
+ @classmethod
+ def tearDownClass(cls):
+ sys.path.remove(cls.site_dir)
+
+ def test_is_submodule_resource(self):
+ self.assertTrue(
+ resources.files(import_module('namespacedata01'))
+ .joinpath('binary.file')
+ .is_file()
+ )
+
+ def test_read_submodule_resource_by_name(self):
+ self.assertTrue(
+ resources.files('namespacedata01').joinpath('binary.file').is_file()
+ )
+
+ def test_submodule_contents(self):
+ contents = names(resources.files(import_module('namespacedata01')))
+ try:
+ contents.remove('__pycache__')
+ except KeyError:
+ pass
+ self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
+
+ def test_submodule_contents_by_name(self):
+ contents = names(resources.files('namespacedata01'))
+ try:
+ contents.remove('__pycache__')
+ except KeyError:
+ pass
+ self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/pkg_resources/_vendor/importlib_resources/tests/update-zips.py b/pkg_resources/_vendor/importlib_resources/tests/update-zips.py
new file mode 100644
index 00000000..9ef0224c
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/update-zips.py
@@ -0,0 +1,53 @@
+"""
+Generate the zip test data files.
+
+Run to build the tests/zipdataNN/ziptestdata.zip files from
+files in tests/dataNN.
+
+Replaces the file with the working copy, but does commit anything
+to the source repo.
+"""
+
+import contextlib
+import os
+import pathlib
+import zipfile
+
+
+def main():
+ """
+ >>> from unittest import mock
+ >>> monkeypatch = getfixture('monkeypatch')
+ >>> monkeypatch.setattr(zipfile, 'ZipFile', mock.MagicMock())
+ >>> print(); main() # print workaround for bpo-32509
+ <BLANKLINE>
+ ...data01... -> ziptestdata/...
+ ...
+ ...data02... -> ziptestdata/...
+ ...
+ """
+ suffixes = '01', '02'
+ tuple(map(generate, suffixes))
+
+
+def generate(suffix):
+ root = pathlib.Path(__file__).parent.relative_to(os.getcwd())
+ zfpath = root / f'zipdata{suffix}/ziptestdata.zip'
+ with zipfile.ZipFile(zfpath, 'w') as zf:
+ for src, rel in walk(root / f'data{suffix}'):
+ dst = 'ziptestdata' / pathlib.PurePosixPath(rel.as_posix())
+ print(src, '->', dst)
+ zf.write(src, dst)
+
+
+def walk(datapath):
+ for dirpath, dirnames, filenames in os.walk(datapath):
+ with contextlib.suppress(KeyError):
+ dirnames.remove('__pycache__')
+ for filename in filenames:
+ res = pathlib.Path(dirpath) / filename
+ rel = res.relative_to(datapath)
+ yield res, rel
+
+
+__name__ == '__main__' and main()
diff --git a/pkg_resources/_vendor/importlib_resources/tests/util.py b/pkg_resources/_vendor/importlib_resources/tests/util.py
new file mode 100644
index 00000000..c6d83e4b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/util.py
@@ -0,0 +1,178 @@
+import abc
+import importlib
+import io
+import sys
+import types
+from pathlib import Path, PurePath
+
+from . import data01
+from . import zipdata01
+from ..abc import ResourceReader
+from ._compat import import_helper
+
+
+from importlib.machinery import ModuleSpec
+
+
+class Reader(ResourceReader):
+ def __init__(self, **kwargs):
+ vars(self).update(kwargs)
+
+ def get_resource_reader(self, package):
+ return self
+
+ def open_resource(self, path):
+ self._path = path
+ if isinstance(self.file, Exception):
+ raise self.file
+ return self.file
+
+ def resource_path(self, path_):
+ self._path = path_
+ if isinstance(self.path, Exception):
+ raise self.path
+ return self.path
+
+ def is_resource(self, path_):
+ self._path = path_
+ if isinstance(self.path, Exception):
+ raise self.path
+
+ def part(entry):
+ return entry.split('/')
+
+ return any(
+ len(parts) == 1 and parts[0] == path_ for parts in map(part, self._contents)
+ )
+
+ def contents(self):
+ if isinstance(self.path, Exception):
+ raise self.path
+ yield from self._contents
+
+
+def create_package_from_loader(loader, is_package=True):
+ name = 'testingpackage'
+ module = types.ModuleType(name)
+ spec = ModuleSpec(name, loader, origin='does-not-exist', is_package=is_package)
+ module.__spec__ = spec
+ module.__loader__ = loader
+ return module
+
+
+def create_package(file=None, path=None, is_package=True, contents=()):
+ return create_package_from_loader(
+ Reader(file=file, path=path, _contents=contents),
+ is_package,
+ )
+
+
+class CommonTests(metaclass=abc.ABCMeta):
+ """
+ Tests shared by test_open, test_path, and test_read.
+ """
+
+ @abc.abstractmethod
+ def execute(self, package, path):
+ """
+ Call the pertinent legacy API function (e.g. open_text, path)
+ on package and path.
+ """
+
+ def test_package_name(self):
+ # Passing in the package name should succeed.
+ self.execute(data01.__name__, 'utf-8.file')
+
+ def test_package_object(self):
+ # Passing in the package itself should succeed.
+ self.execute(data01, 'utf-8.file')
+
+ def test_string_path(self):
+ # Passing in a string for the path should succeed.
+ path = 'utf-8.file'
+ self.execute(data01, path)
+
+ def test_pathlib_path(self):
+ # Passing in a pathlib.PurePath object for the path should succeed.
+ path = PurePath('utf-8.file')
+ self.execute(data01, path)
+
+ def test_importing_module_as_side_effect(self):
+ # The anchor package can already be imported.
+ del sys.modules[data01.__name__]
+ self.execute(data01.__name__, 'utf-8.file')
+
+ def test_non_package_by_name(self):
+ # The anchor package cannot be a module.
+ with self.assertRaises(TypeError):
+ self.execute(__name__, 'utf-8.file')
+
+ def test_non_package_by_package(self):
+ # The anchor package cannot be a module.
+ with self.assertRaises(TypeError):
+ module = sys.modules['importlib_resources.tests.util']
+ self.execute(module, 'utf-8.file')
+
+ def test_missing_path(self):
+ # Attempting to open or read or request the path for a
+ # non-existent path should succeed if open_resource
+ # can return a viable data stream.
+ bytes_data = io.BytesIO(b'Hello, world!')
+ package = create_package(file=bytes_data, path=FileNotFoundError())
+ self.execute(package, 'utf-8.file')
+ self.assertEqual(package.__loader__._path, 'utf-8.file')
+
+ def test_extant_path(self):
+ # Attempting to open or read or request the path when the
+ # path does exist should still succeed. Does not assert
+ # anything about the result.
+ bytes_data = io.BytesIO(b'Hello, world!')
+ # any path that exists
+ path = __file__
+ package = create_package(file=bytes_data, path=path)
+ self.execute(package, 'utf-8.file')
+ self.assertEqual(package.__loader__._path, 'utf-8.file')
+
+ def test_useless_loader(self):
+ package = create_package(file=FileNotFoundError(), path=FileNotFoundError())
+ with self.assertRaises(FileNotFoundError):
+ self.execute(package, 'utf-8.file')
+
+
+class ZipSetupBase:
+ ZIP_MODULE = None
+
+ @classmethod
+ def setUpClass(cls):
+ data_path = Path(cls.ZIP_MODULE.__file__)
+ data_dir = data_path.parent
+ cls._zip_path = str(data_dir / 'ziptestdata.zip')
+ sys.path.append(cls._zip_path)
+ cls.data = importlib.import_module('ziptestdata')
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ sys.path.remove(cls._zip_path)
+ except ValueError:
+ pass
+
+ try:
+ del sys.path_importer_cache[cls._zip_path]
+ del sys.modules[cls.data.__name__]
+ except KeyError:
+ pass
+
+ try:
+ del cls.data
+ del cls._zip_path
+ except AttributeError:
+ pass
+
+ def setUp(self):
+ modules = import_helper.modules_setup()
+ self.addCleanup(import_helper.modules_cleanup, *modules)
+
+
+class ZipSetup(ZipSetupBase):
+ ZIP_MODULE = zipdata01 # type: ignore
diff --git a/pkg_resources/_vendor/importlib_resources/tests/zipdata01/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/zipdata01/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/zipdata01/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zip b/pkg_resources/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zip
new file mode 100644
index 00000000..9a3bb073
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zip
Binary files differ
diff --git a/pkg_resources/_vendor/importlib_resources/tests/zipdata02/__init__.py b/pkg_resources/_vendor/importlib_resources/tests/zipdata02/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/zipdata02/__init__.py
diff --git a/pkg_resources/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zip b/pkg_resources/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zip
new file mode 100644
index 00000000..d63ff512
--- /dev/null
+++ b/pkg_resources/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zip
Binary files differ
diff --git a/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/LICENSE b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/METADATA b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/METADATA
new file mode 100644
index 00000000..908711b7
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/METADATA
@@ -0,0 +1,52 @@
+Metadata-Version: 2.1
+Name: jaraco.context
+Version: 4.1.1
+Summary: Context managers by jaraco
+Home-page: https://github.com/jaraco/jaraco.context
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.6
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.context.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.context.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.context
+
+.. image:: https://github.com/jaraco/jaraco.context/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.context/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracocontext/badge/?version=latest
+ :target: https://jaracocontext.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+
diff --git a/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/RECORD b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/RECORD
new file mode 100644
index 00000000..f40d48c7
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/RECORD
@@ -0,0 +1,8 @@
+jaraco.context-4.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+jaraco.context-4.1.1.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+jaraco.context-4.1.1.dist-info/METADATA,sha256=bvqDGCk6Z7TkohUqr5XZm19SbF9mVxrtXjN6uF_BAMQ,2031
+jaraco.context-4.1.1.dist-info/RECORD,,
+jaraco.context-4.1.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+jaraco.context-4.1.1.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+jaraco/__pycache__/context.cpython-310.pyc,,
+jaraco/context.py,sha256=7X1tpCLc5EN45iWGzGcsH0Unx62REIkvtRvglj0SiUA,5420
diff --git a/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/WHEEL b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt
new file mode 100644
index 00000000..f6205a5f
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/METADATA b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/METADATA
new file mode 100644
index 00000000..12dfbdd0
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/METADATA
@@ -0,0 +1,58 @@
+Metadata-Version: 2.1
+Name: jaraco.functools
+Version: 3.5.0
+Summary: Functools like those found in stdlib
+Home-page: https://github.com/jaraco/jaraco.functools
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: more-itertools
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: jaraco.classes ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.functools.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.functools.svg
+
+.. image:: https://img.shields.io/travis/jaraco/jaraco.functools/master.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.functools
+
+.. image:: https://github.com/jaraco/jaraco.functools/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.functools/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracofunctools/badge/?version=latest
+ :target: https://jaracofunctools.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+Additional functools in the spirit of stdlib's functools.
+
+
diff --git a/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/RECORD b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/RECORD
new file mode 100644
index 00000000..fbda3d1f
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/RECORD
@@ -0,0 +1,8 @@
+jaraco.functools-3.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+jaraco.functools-3.5.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+jaraco.functools-3.5.0.dist-info/METADATA,sha256=cE9C7u9bo_GjLAuw4nML67a25kUaPDiHn4j03lG4jd0,2276
+jaraco.functools-3.5.0.dist-info/RECORD,,
+jaraco.functools-3.5.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+jaraco.functools-3.5.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+jaraco/__pycache__/functools.cpython-310.pyc,,
+jaraco/functools.py,sha256=PtEHbXZstgVJrwje4GvJOsz5pEbgslOcgEn2EJNpr2c,13494
diff --git a/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt
new file mode 100644
index 00000000..f6205a5f
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/LICENSE b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/METADATA b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/METADATA
new file mode 100644
index 00000000..615a50a4
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/METADATA
@@ -0,0 +1,55 @@
+Metadata-Version: 2.1
+Name: jaraco.text
+Version: 3.7.0
+Summary: Module for text manipulation
+Home-page: https://github.com/jaraco/jaraco.text
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.6
+License-File: LICENSE
+Requires-Dist: jaraco.functools
+Requires-Dist: jaraco.context (>=4.1)
+Requires-Dist: importlib-resources ; python_version < "3.9"
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.text.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.text.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.text
+
+.. image:: https://github.com/jaraco/jaraco.text/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.text/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracotext/badge/?version=latest
+ :target: https://jaracotext.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+
diff --git a/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/RECORD b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/RECORD
new file mode 100644
index 00000000..916ad7d3
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/RECORD
@@ -0,0 +1,10 @@
+jaraco.text-3.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+jaraco.text-3.7.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+jaraco.text-3.7.0.dist-info/METADATA,sha256=5mcR1dY0cJNrM-VIkAFkpjOgvgzmq6nM1GfD0gwTIhs,2136
+jaraco.text-3.7.0.dist-info/RECORD,,
+jaraco.text-3.7.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+jaraco.text-3.7.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+jaraco.text-3.7.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+jaraco/text/Lorem ipsum.txt,sha256=N_7c_79zxOufBY9HZ3yzMgOkNv-TkOTTio4BydrSjgs,1335
+jaraco/text/__init__.py,sha256=I56MW2ZFwPrYXIxzqxMBe2A1t-T4uZBgEgAKe9-JoqM,15538
+jaraco/text/__pycache__/__init__.cpython-310.pyc,,
diff --git a/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED
diff --git a/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/WHEEL b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/WHEEL
new file mode 100644
index 00000000..becc9a66
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt
new file mode 100644
index 00000000..f6205a5f
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/pkg_resources/_vendor/jaraco/__init__.py b/pkg_resources/_vendor/jaraco/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco/__init__.py
diff --git a/pkg_resources/_vendor/jaraco/context.py b/pkg_resources/_vendor/jaraco/context.py
new file mode 100644
index 00000000..87a4e3dc
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco/context.py
@@ -0,0 +1,213 @@
+import os
+import subprocess
+import contextlib
+import functools
+import tempfile
+import shutil
+import operator
+
+
+@contextlib.contextmanager
+def pushd(dir):
+ orig = os.getcwd()
+ os.chdir(dir)
+ try:
+ yield dir
+ finally:
+ os.chdir(orig)
+
+
+@contextlib.contextmanager
+def tarball_context(url, target_dir=None, runner=None, pushd=pushd):
+ """
+ Get a tarball, extract it, change to that directory, yield, then
+ clean up.
+ `runner` is the function to invoke commands.
+ `pushd` is a context manager for changing the directory.
+ """
+ if target_dir is None:
+ target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '')
+ if runner is None:
+ runner = functools.partial(subprocess.check_call, shell=True)
+ # 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.
+ runner('mkdir {target_dir}'.format(**vars()))
+ try:
+ getter = 'wget {url} -O -'
+ extract = 'tar x{compression} --strip-components=1 -C {target_dir}'
+ cmd = ' | '.join((getter, extract))
+ runner(cmd.format(compression=infer_compression(url), **vars()))
+ with pushd(target_dir):
+ yield target_dir
+ finally:
+ runner('rm -Rf {target_dir}'.format(**vars()))
+
+
+def infer_compression(url):
+ """
+ Given a URL or filename, infer the compression code for tar.
+ """
+ # 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.
+ """
+ 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
+
+
+@contextlib.contextmanager
+def null():
+ yield
+
+
+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
+
+ >>> 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()
+ """
diff --git a/pkg_resources/_vendor/jaraco/functools.py b/pkg_resources/_vendor/jaraco/functools.py
new file mode 100644
index 00000000..a3fea3a1
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco/functools.py
@@ -0,0 +1,525 @@
+import functools
+import time
+import inspect
+import collections
+import types
+import itertools
+
+import pkg_resources.extern.more_itertools
+
+from typing import Callable, TypeVar
+
+
+CallableT = TypeVar("CallableT", bound=Callable[..., object])
+
+
+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 method_caller(method_name, *args, **kwargs):
+ """
+ Return a function that will call a named method on the
+ target object with optional positional and keyword
+ arguments.
+
+ >>> lower = method_caller('lower')
+ >>> lower('MyString')
+ 'mystring'
+ """
+
+ def call_method(target):
+ func = getattr(target, method_name)
+ return func(*args, **kwargs)
+
+ return call_method
+
+
+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: CallableT,
+ cache_wrapper: Callable[
+ [CallableT], CallableT
+ ] = functools.lru_cache(), # type: ignore[assignment]
+) -> CallableT:
+ """
+ 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: object, *args: object, **kwargs: object) -> object:
+ # it's the first call, replace the method with a cached, bound method
+ bound_method: CallableT = types.MethodType( # type: ignore[assignment]
+ 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 # type: ignore[attr-defined]
+
+ return ( # type: ignore[return-value]
+ _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
+
+ 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 call_aside(f, *args, **kwargs):
+ """
+ Call a function for its side effect after initialization.
+
+ >>> @call_aside
+ ... def func(): print("called")
+ called
+ >>> func()
+ called
+
+ Use functools.partial to pass parameters to the initial call
+
+ >>> @functools.partial(call_aside, 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, type=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
+
+
+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 attempt 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 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
diff --git a/pkg_resources/_vendor/jaraco/text/Lorem ipsum.txt b/pkg_resources/_vendor/jaraco/text/Lorem ipsum.txt
new file mode 100644
index 00000000..986f944b
--- /dev/null
+++ b/pkg_resources/_vendor/jaraco/text/Lorem ipsum.txt
@@ -0,0 +1,2 @@
+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.
diff --git a/pkg_resources/_vendor/jaraco/text/__init__.py b/pkg_resources/_vendor/jaraco/text/__init__.py
new file mode 100644
index 00000000..c466378c
--- /dev/null
+++ b/pkg_resources/_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 pkg_resources.extern.importlib_resources import files # type: ignore
+
+from pkg_resources.extern.jaraco.functools import compose, method_cache
+from pkg_resources.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.
+ <BLANKLINE>
+ 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 <bold>stormy</bold> day in paradise'))
+ A stormy day in paradise
+
+ >>> print(simple_html_strip('Somebody <!-- do not --> tell the truth.'))
+ Somebody tell the truth.
+
+ >>> print(simple_html_strip('What about<br/>\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/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/INSTALLER b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/LICENSE b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/LICENSE
new file mode 100644
index 00000000..0a523bec
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Erik Rose
+
+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/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/METADATA b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/METADATA
new file mode 100644
index 00000000..9efacdd7
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/METADATA
@@ -0,0 +1,521 @@
+Metadata-Version: 2.1
+Name: more-itertools
+Version: 8.12.0
+Summary: More routines for operating on iterables, beyond itertools
+Home-page: https://github.com/more-itertools/more-itertools
+Author: Erik Rose
+Author-email: erikrose@grinchcentral.com
+License: MIT
+Keywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries
+Requires-Python: >=3.5
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+
+==============
+More Itertools
+==============
+
+.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest
+ :target: https://more-itertools.readthedocs.io/en/stable/
+
+Python's ``itertools`` library is a gem - you can compose elegant solutions
+for a variety of problems with the functions it provides. In ``more-itertools``
+we collect additional building blocks, recipes, and routines for working with
+Python iterables.
+
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Grouping | `chunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_, |
+| | `ichunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_, |
+| | `sliced <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliced>`_, |
+| | `distribute <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distribute>`_, |
+| | `divide <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.divide>`_, |
+| | `split_at <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_at>`_, |
+| | `split_before <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_before>`_, |
+| | `split_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_after>`_, |
+| | `split_into <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_into>`_, |
+| | `split_when <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_when>`_, |
+| | `bucket <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.bucket>`_, |
+| | `unzip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unzip>`_, |
+| | `grouper <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper>`_, |
+| | `partition <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partition>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Lookahead and lookback | `spy <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy>`_, |
+| | `peekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable>`_, |
+| | `seekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.seekable>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Windowing | `windowed <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed>`_, |
+| | `substrings <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings>`_, |
+| | `substrings_indexes <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings_indexes>`_, |
+| | `stagger <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.stagger>`_, |
+| | `windowed_complete <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed_complete>`_, |
+| | `pairwise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pairwise>`_, |
+| | `triplewise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.triplewise>`_, |
+| | `sliding_window <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliding_window>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Augmenting | `count_cycle <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.count_cycle>`_, |
+| | `intersperse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.intersperse>`_, |
+| | `padded <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padded>`_, |
+| | `mark_ends <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.mark_ends>`_, |
+| | `repeat_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_last>`_, |
+| | `adjacent <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.adjacent>`_, |
+| | `groupby_transform <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.groupby_transform>`_, |
+| | `pad_none <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pad_none>`_, |
+| | `ncycles <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ncycles>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combining | `collapse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse>`_, |
+| | `sort_together <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sort_together>`_, |
+| | `interleave <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_, |
+| | `interleave_longest <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_, |
+| | `interleave_evenly <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_evenly>`_, |
+| | `zip_offset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_, |
+| | `zip_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_, |
+| | `zip_broadcast <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_broadcast>`_, |
+| | `dotproduct <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_, |
+| | `convolve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.convolve>`_, |
+| | `flatten <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_, |
+| | `roundrobin <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_, |
+| | `prepend <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.prepend>`_, |
+| | `value_chain <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.value_chain>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Summarizing | `ilen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ilen>`_, |
+| | `unique_to_each <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_to_each>`_, |
+| | `sample <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sample>`_, |
+| | `consecutive_groups <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consecutive_groups>`_, |
+| | `run_length <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.run_length>`_, |
+| | `map_reduce <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_reduce>`_, |
+| | `exactly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.exactly_n>`_, |
+| | `is_sorted <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_sorted>`_, |
+| | `all_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_equal>`_, |
+| | `all_unique <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_unique>`_, |
+| | `minmax <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.minmax>`_, |
+| | `first_true <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first_true>`_, |
+| | `quantify <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.quantify>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Selecting | `islice_extended <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.islice_extended>`_, |
+| | `first <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first>`_, |
+| | `last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.last>`_, |
+| | `one <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one>`_, |
+| | `only <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.only>`_, |
+| | `strictly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strictly_n>`_, |
+| | `strip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strip>`_, |
+| | `lstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.lstrip>`_, |
+| | `rstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rstrip>`_, |
+| | `filter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.filter_except>`_, |
+| | `map_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_except>`_, |
+| | `nth_or_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_or_last>`_, |
+| | `unique_in_window <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_in_window>`_, |
+| | `before_and_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.before_and_after>`_, |
+| | `nth <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth>`_, |
+| | `take <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.take>`_, |
+| | `tail <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tail>`_, |
+| | `unique_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertoo ls.unique_everseen>`_, |
+| | `unique_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_justseen>`_, |
+| | `duplicates_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.duplicates_everseen>`_, |
+| | `duplicates_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.duplicates_justseen>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combinatorics | `distinct_permutations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_permutations>`_, |
+| | `distinct_combinations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_combinations>`_, |
+| | `circular_shifts <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.circular_shifts>`_, |
+| | `partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partitions>`_, |
+| | `set_partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.set_partitions>`_, |
+| | `product_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.product_index>`_, |
+| | `combination_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.combination_index>`_, |
+| | `permutation_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.permutation_index>`_, |
+| | `powerset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset>`_, |
+| | `random_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product>`_, |
+| | `random_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation>`_, |
+| | `random_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination>`_, |
+| | `random_combination_with_replacement <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination_with_replacement>`_, |
+| | `nth_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_product>`_, |
+| | `nth_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_permutation>`_, |
+| | `nth_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_combination>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Wrapping | `always_iterable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_, |
+| | `always_reversible <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_reversible>`_, |
+| | `countable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.countable>`_, |
+| | `consumer <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consumer>`_, |
+| | `with_iter <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.with_iter>`_, |
+| | `iter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iter_except>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Others | `locate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.locate>`_, |
+| | `rlocate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rlocate>`_, |
+| | `replace <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.replace>`_, |
+| | `numeric_range <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.numeric_range>`_, |
+| | `side_effect <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.side_effect>`_, |
+| | `iterate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iterate>`_, |
+| | `difference <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.difference>`_, |
+| | `make_decorator <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.make_decorator>`_, |
+| | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, |
+| | `time_limited <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.time_limited>`_, |
+| | `consume <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume>`_, |
+| | `tabulate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tabulate>`_, |
+| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_ |
++------------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+
+Getting started
+===============
+
+To get started, install the library with `pip <https://pip.pypa.io/en/stable/>`_:
+
+.. code-block:: shell
+
+ pip install more-itertools
+
+The recipes from the `itertools docs <https://docs.python.org/3/library/itertools.html#itertools-recipes>`_
+are included in the top-level package:
+
+.. code-block:: python
+
+ >>> from more_itertools import flatten
+ >>> iterable = [(0, 1), (2, 3)]
+ >>> list(flatten(iterable))
+ [0, 1, 2, 3]
+
+Several new recipes are available as well:
+
+.. code-block:: python
+
+ >>> from more_itertools import chunked
+ >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8]
+ >>> list(chunked(iterable, 3))
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
+
+ >>> from more_itertools import spy
+ >>> iterable = (x * x for x in range(1, 6))
+ >>> head, iterable = spy(iterable, n=3)
+ >>> list(head)
+ [1, 4, 9]
+ >>> list(iterable)
+ [1, 4, 9, 16, 25]
+
+
+
+For the full listing of functions, see the `API documentation <https://more-itertools.readthedocs.io/en/stable/api.html>`_.
+
+
+Links elsewhere
+===============
+
+Blog posts about ``more-itertools``:
+
+* `Yo, I heard you like decorators <https://www.bbayles.com/index/decorator_factory>`__
+* `Tour of Python Itertools <https://martinheinz.dev/blog/16>`__ (`Alternate <https://dev.to/martinheinz/tour-of-python-itertools-4122>`__)
+* `Real-World Python More Itertools <https://www.gidware.com/real-world-more-itertools/>`_
+
+
+Development
+===========
+
+``more-itertools`` is maintained by `@erikrose <https://github.com/erikrose>`_
+and `@bbayles <https://github.com/bbayles>`_, with help from `many others <https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
+If you have a problem or suggestion, please file a bug or pull request in this
+repository. Thanks for contributing!
+
+
+Version History
+===============
+
+
+ :noindex:
+
+8.12.0
+------
+
+* Bug fixes
+ * Some documentation issues were fixed (thanks to Masynchin, spookylukey, astrojuanlu, and stephengmatthews)
+ * Python 3.5 support was temporarily restored (thanks to mattbonnell)
+
+8.11.0
+------
+
+* New functions
+ * The before_and_after, sliding_window, and triplewise recipes from the Python 3.10 docs were added
+ * duplicates_everseen and duplicates_justseen (thanks to OrBin and DavidPratt512)
+ * minmax (thanks to Ricocotam, MSeifert04, and ruancomelli)
+ * strictly_n (thanks to hwalinga and NotWearingPants)
+ * unique_in_window
+
+* Changes to existing functions
+ * groupby_transform had its type stub improved (thanks to mjk4 and ruancomelli)
+ * is_sorted now accepts a ``strict`` parameter (thanks to Dutcho and ruancomelli)
+ * zip_broadcast was updated to fix a bug (thanks to kalekundert)
+
+8.10.0
+------
+
+* Changes to existing functions
+ * The type stub for iter_except was improved (thanks to MarcinKonowalczyk)
+
+* Other changes:
+ * Type stubs now ship with the source release (thanks to saaketp)
+ * The Sphinx docs were improved (thanks to MarcinKonowalczyk)
+
+8.9.0
+-----
+
+* New functions
+ * interleave_evenly (thanks to mbugert)
+ * repeat_each (thanks to FinalSh4re)
+ * chunked_even (thanks to valtron)
+ * map_if (thanks to sassbalint)
+ * zip_broadcast (thanks to kalekundert)
+
+* Changes to existing functions
+ * The type stub for chunked was improved (thanks to PhilMacKay)
+ * The type stubs for zip_equal and `zip_offset` were improved (thanks to maffoo)
+ * Building Sphinx docs locally was improved (thanks to MarcinKonowalczyk)
+
+8.8.0
+-----
+
+* New functions
+ * countable (thanks to krzysieq)
+
+* Changes to existing functions
+ * split_before was updated to handle empy collections (thanks to TiunovNN)
+ * unique_everseen got a performance boost (thanks to Numerlor)
+ * The type hint for value_chain was corrected (thanks to vr2262)
+
+8.7.0
+-----
+
+* New functions
+ * convolve (from the Python itertools docs)
+ * product_index, combination_index, and permutation_index (thanks to N8Brooks)
+ * value_chain (thanks to jenstroeger)
+
+* Changes to existing functions
+ * distinct_combinations now uses a non-recursive algorithm (thanks to knutdrand)
+ * pad_none is now the preferred name for padnone, though the latter remains available.
+ * pairwise will now use the Python standard library implementation on Python 3.10+
+ * sort_together now accepts a ``key`` argument (thanks to brianmaissy)
+ * seekable now has a ``peek`` method, and can indicate whether the iterator it's wrapping is exhausted (thanks to gsakkis)
+ * time_limited can now indicate whether its iterator has expired (thanks to roysmith)
+ * The implementation of unique_everseen was improved (thanks to plammens)
+
+* Other changes:
+ * Various documentation updates (thanks to cthoyt, Evantm, and cyphase)
+
+8.6.0
+-----
+
+* New itertools
+ * all_unique (thanks to brianmaissy)
+ * nth_product and nth_permutation (thanks to N8Brooks)
+
+* Changes to existing itertools
+ * chunked and sliced now accept a ``strict`` parameter (thanks to shlomif and jtwool)
+
+* Other changes
+ * Python 3.5 has reached its end of life and is no longer supported.
+ * Python 3.9 is officially supported.
+ * Various documentation fixes (thanks to timgates42)
+
+8.5.0
+-----
+
+* New itertools
+ * windowed_complete (thanks to MarcinKonowalczyk)
+
+* Changes to existing itertools:
+ * The is_sorted implementation was improved (thanks to cool-RR)
+ * The groupby_transform now accepts a ``reducefunc`` parameter.
+ * The last implementation was improved (thanks to brianmaissy)
+
+* Other changes
+ * Various documentation fixes (thanks to craigrosie, samuelstjean, PiCT0)
+ * The tests for distinct_combinations were improved (thanks to Minabsapi)
+ * Automated tests now run on GitHub Actions. All commits now check:
+ * That unit tests pass
+ * That the examples in docstrings work
+ * That test coverage remains high (using `coverage`)
+ * For linting errors (using `flake8`)
+ * For consistent style (using `black`)
+ * That the type stubs work (using `mypy`)
+ * That the docs build correctly (using `sphinx`)
+ * That packages build correctly (using `twine`)
+
+8.4.0
+-----
+
+* New itertools
+ * mark_ends (thanks to kalekundert)
+ * is_sorted
+
+* Changes to existing itertools:
+ * islice_extended can now be used with real slices (thanks to cool-RR)
+ * The implementations for filter_except and map_except were improved (thanks to SergBobrovsky)
+
+* Other changes
+ * Automated tests now enforce code style (using `black <https://github.com/psf/black>`__)
+ * The various signatures of islice_extended and numeric_range now appear in the docs (thanks to dsfulf)
+ * The test configuration for mypy was updated (thanks to blueyed)
+
+
+8.3.0
+-----
+
+* New itertools
+ * zip_equal (thanks to frankier and alexmojaki)
+
+* Changes to existing itertools:
+ * split_at, split_before, split_after, and split_when all got a ``maxsplit`` paramter (thanks to jferard and ilai-deutel)
+ * split_at now accepts a ``keep_separator`` parameter (thanks to jferard)
+ * distinct_permutations can now generate ``r``-length permutations (thanks to SergBobrovsky and ilai-deutel)
+ * The windowed implementation was improved (thanks to SergBobrovsky)
+ * The spy implementation was improved (thanks to has2k1)
+
+* Other changes
+ * Type stubs are now tested with ``stubtest`` (thanks to ilai-deutel)
+ * Tests now run with ``python -m unittest`` instead of ``python setup.py test`` (thanks to jdufresne)
+
+8.2.0
+-----
+
+* Bug fixes
+ * The .pyi files for typing were updated. (thanks to blueyed and ilai-deutel)
+
+* Changes to existing itertools:
+ * numeric_range now behaves more like the built-in range. (thanks to jferard)
+ * bucket now allows for enumerating keys. (thanks to alexchandel)
+ * sliced now should now work for numpy arrays. (thanks to sswingle)
+ * seekable now has a ``maxlen`` parameter.
+
+8.1.0
+-----
+
+* Bug fixes
+ * partition works with ``pred=None`` again. (thanks to MSeifert04)
+
+* New itertools
+ * sample (thanks to tommyod)
+ * nth_or_last (thanks to d-ryzhikov)
+
+* Changes to existing itertools:
+ * The implementation for divide was improved. (thanks to jferard)
+
+8.0.2
+-----
+
+* Bug fixes
+ * The type stub files are now part of the wheel distribution (thanks to keisheiled)
+
+8.0.1
+-----
+
+* Bug fixes
+ * The type stub files now work for functions imported from the
+ root package (thanks to keisheiled)
+
+8.0.0
+-----
+
+* New itertools and other additions
+ * This library now ships type hints for use with mypy.
+ (thanks to ilai-deutel for the implementation, and to gabbard and fmagin for assistance)
+ * split_when (thanks to jferard)
+ * repeat_last (thanks to d-ryzhikov)
+
+* Changes to existing itertools:
+ * The implementation for set_partitions was improved. (thanks to jferard)
+ * partition was optimized for expensive predicates. (thanks to stevecj)
+ * unique_everseen and groupby_transform were re-factored. (thanks to SergBobrovsky)
+ * The implementation for difference was improved. (thanks to Jabbey92)
+
+* Other changes
+ * Python 3.4 has reached its end of life and is no longer supported.
+ * Python 3.8 is officially supported. (thanks to jdufresne)
+ * The ``collate`` function has been deprecated.
+ It raises a ``DeprecationWarning`` if used, and will be removed in a future release.
+ * one and only now provide more informative error messages. (thanks to gabbard)
+ * Unit tests were moved outside of the main package (thanks to jdufresne)
+ * Various documentation fixes (thanks to kriomant, gabbard, jdufresne)
+
+
+7.2.0
+-----
+
+* New itertools
+ * distinct_combinations
+ * set_partitions (thanks to kbarrett)
+ * filter_except
+ * map_except
+
+7.1.0
+-----
+
+* New itertools
+ * ichunked (thanks davebelais and youtux)
+ * only (thanks jaraco)
+
+* Changes to existing itertools:
+ * numeric_range now supports ranges specified by
+ ``datetime.datetime`` and ``datetime.timedelta`` objects (thanks to MSeifert04 for tests).
+ * difference now supports an *initial* keyword argument.
+
+
+* Other changes
+ * Various documentation fixes (thanks raimon49, pylang)
+
+7.0.0
+-----
+
+* New itertools:
+ * time_limited
+ * partitions (thanks to rominf and Saluev)
+ * substrings_indexes (thanks to rominf)
+
+* Changes to existing itertools:
+ * collapse now treats ``bytes`` objects the same as ``str`` objects. (thanks to Sweenpet)
+
+The major version update is due to the change in the default behavior of
+collapse. It now treats ``bytes`` objects the same as ``str`` objects.
+This aligns its behavior with always_iterable.
+
+.. code-block:: python
+
+ >>> from more_itertools import collapse
+ >>> iterable = [[1, 2], b'345', [6]]
+ >>> print(list(collapse(iterable)))
+ [1, 2, b'345', 6]
+
+6.0.0
+-----
+
+* Major changes:
+ * Python 2.7 is no longer supported. The 5.0.0 release will be the last
+ version targeting Python 2.7.
+ * All future releases will target the active versions of Python 3.
+ As of 2019, those are Python 3.4 and above.
+ * The ``six`` library is no longer a dependency.
+ * The accumulate function is no longer part of this library. You
+ may import a better version from the standard ``itertools`` module.
+
+* Changes to existing itertools:
+ * The order of the parameters in grouper have changed to match
+ the latest recipe in the itertools documentation. Use of the old order
+ will be supported in this release, but emit a ``DeprecationWarning``.
+ The legacy behavior will be dropped in a future release. (thanks to jaraco)
+ * distinct_permutations was improved (thanks to jferard - see also `permutations with unique values <https://stackoverflow.com/questions/6284396/permutations-with-unique-values>`_ at StackOverflow.)
+ * An unused parameter was removed from substrings. (thanks to pylang)
+
+* Other changes:
+ * The docs for unique_everseen were improved. (thanks to jferard and MSeifert04)
+ * Several Python 2-isms were removed. (thanks to jaraco, MSeifert04, and hugovk)
+
+
diff --git a/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/RECORD b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/RECORD
new file mode 100644
index 00000000..44847291
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/RECORD
@@ -0,0 +1,16 @@
+more_itertools-8.12.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+more_itertools-8.12.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053
+more_itertools-8.12.0.dist-info/METADATA,sha256=QCCEcisEPr7iSfBIKCukhP-FbG9ehMK8tDIliZ3FBDc,39405
+more_itertools-8.12.0.dist-info/RECORD,,
+more_itertools-8.12.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+more_itertools-8.12.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15
+more_itertools/__init__.py,sha256=ZQYu_9H6stSG7viUgT32TFqslqcZwq82kWRZooKiI8Y,83
+more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43
+more_itertools/__pycache__/__init__.cpython-310.pyc,,
+more_itertools/__pycache__/more.cpython-310.pyc,,
+more_itertools/__pycache__/recipes.cpython-310.pyc,,
+more_itertools/more.py,sha256=jSrvV9BK-XKa4x7MPPp9yWYRDtRgR5h7yryEqHMU4mg,132578
+more_itertools/more.pyi,sha256=kWOkRKx0V8ZwC1D2j0c0DUfy56dazzpmRcm5ZuY_aqo,20006
+more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+more_itertools/recipes.py,sha256=N6aCDwoIPvE-aiqpGU-nbFwqiM3X8MKRcxBM84naW88,18410
+more_itertools/recipes.pyi,sha256=Lx3vb0p_vY7rF8MQuguvOcVaS9qd1WRL8JO_qVo7hiY,3925
diff --git a/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/WHEEL b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/top_level.txt b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/top_level.txt
new file mode 100644
index 00000000..a5035bef
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools-8.12.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+more_itertools
diff --git a/pkg_resources/_vendor/more_itertools/__init__.py b/pkg_resources/_vendor/more_itertools/__init__.py
new file mode 100644
index 00000000..ea38bef1
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools/__init__.py
@@ -0,0 +1,4 @@
+from .more import * # noqa
+from .recipes import * # noqa
+
+__version__ = '8.12.0'
diff --git a/pkg_resources/_vendor/more_itertools/__init__.pyi b/pkg_resources/_vendor/more_itertools/__init__.pyi
new file mode 100644
index 00000000..96f6e36c
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools/__init__.pyi
@@ -0,0 +1,2 @@
+from .more import *
+from .recipes import *
diff --git a/pkg_resources/_vendor/more_itertools/more.py b/pkg_resources/_vendor/more_itertools/more.py
new file mode 100644
index 00000000..6b6a5cab
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools/more.py
@@ -0,0 +1,4316 @@
+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, ge, le
+from sys import hexversion, maxsize
+from time import monotonic
+
+from .recipes import (
+ consume,
+ flatten,
+ pairwise,
+ powerset,
+ take,
+ unique_everseen,
+)
+
+__all__ = [
+ 'AbortThread',
+ 'SequenceView',
+ 'UnequalIterablesError',
+ 'adjacent',
+ 'all_unique',
+ 'always_iterable',
+ 'always_reversible',
+ 'bucket',
+ 'callback_iter',
+ 'chunked',
+ 'chunked_even',
+ 'circular_shifts',
+ 'collapse',
+ 'collate',
+ 'combination_index',
+ 'consecutive_groups',
+ 'consumer',
+ 'count_cycle',
+ 'countable',
+ 'difference',
+ 'distinct_combinations',
+ 'distinct_permutations',
+ 'distribute',
+ 'divide',
+ 'duplicates_everseen',
+ 'duplicates_justseen',
+ 'exactly_n',
+ 'filter_except',
+ 'first',
+ 'groupby_transform',
+ 'ichunked',
+ 'ilen',
+ 'interleave',
+ 'interleave_evenly',
+ 'interleave_longest',
+ 'intersperse',
+ 'is_sorted',
+ 'islice_extended',
+ 'iterate',
+ 'last',
+ 'locate',
+ 'lstrip',
+ 'make_decorator',
+ 'map_except',
+ 'map_if',
+ 'map_reduce',
+ 'mark_ends',
+ 'minmax',
+ 'nth_or_last',
+ 'nth_permutation',
+ 'nth_product',
+ 'numeric_range',
+ 'one',
+ 'only',
+ 'padded',
+ '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',
+ 'time_limited',
+ 'unique_in_window',
+ 'unique_to_each',
+ 'unzip',
+ 'value_chain',
+ 'windowed',
+ 'windowed_complete',
+ 'with_iter',
+ 'zip_broadcast',
+ 'zip_equal',
+ 'zip_offset',
+]
+
+
+_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:
+ 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)``.
+
+ """
+ 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 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']
+
+ 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 < 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 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.
+ 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_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
+ )
+
+
+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:
+ 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_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 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 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
+ 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
+
+
+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
+
+ buffer = []
+ iterator = iter(iterable)
+
+ # Yield num_full lists of full_size
+ for x in iterator:
+ buffer.append(x)
+ if len(buffer) == full_size:
+ yield buffer
+ buffer = []
+ num_full -= 1
+ if num_full <= 0:
+ break
+
+ # Yield num_partial lists of partial_size
+ for x in iterator:
+ buffer.append(x)
+ if len(buffer) == partial_size:
+ yield buffer
+ buffer = []
+ num_partial -= 1
+
+
+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 lengthss.
+ """
+
+ 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
+
+ iterables, iterable_positions = [], []
+ scalars, scalar_positions = [], []
+ for i, obj in enumerate(objects):
+ if is_scalar(obj):
+ scalars.append(obj)
+ scalar_positions.append(i)
+ else:
+ iterables.append(iter(obj))
+ iterable_positions.append(i)
+
+ if len(scalars) == size:
+ yield tuple(objects)
+ return
+
+ zipper = _zip_equal if strict else zip
+ for item in zipper(*iterables):
+ new_item = [None] * size
+
+ for i, elem in zip(iterable_positions, item):
+ new_item[i] = elem
+
+ for i, elem in zip(scalar_positions, scalars):
+ new_item[i] = elem
+
+ 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)
+ uniques = set()
+ use_key = key is not None
+
+ for item in iterable:
+ k = key(item) if use_key else item
+ if k in uniques:
+ continue
+
+ if len(uniques) == n:
+ uniques.discard(window[0])
+
+ uniques.add(k)
+ window.append(k)
+
+ yield item
+
+
+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 analagous 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 analagous to :func:`unique_justseen`.
+
+ """
+ return flatten(
+ map(
+ lambda group_tuple: islice_extended(group_tuple[1])[1:],
+ groupby(iterable, key),
+ )
+ )
+
+
+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 <http://code.activestate.com/recipes/577916/>`__ 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
diff --git a/pkg_resources/_vendor/more_itertools/more.pyi b/pkg_resources/_vendor/more_itertools/more.pyi
new file mode 100644
index 00000000..fe7d4bdd
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools/more.pyi
@@ -0,0 +1,664 @@
+"""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')
+_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[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: Optional[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 raise_(exception: _Raisable, *args: Any) -> None: ...
+def strictly_n(
+ iterable: Iterable[_T],
+ n: int,
+ too_short: Optional[_GenFn] = ...,
+ too_long: Optional[_GenFn] = ...,
+) -> List[_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 interleave_evenly(
+ iterables: List[Iterable[_T]], lengths: Optional[List[int]] = ...
+) -> 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: ...
+
+@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[Optional[_T1]]]: ...
+@overload
+def zip_offset(
+ __iter1: Iterable[_T1],
+ __iter2: Iterable[_T2],
+ *,
+ offsets: _SizedIterable[int],
+ longest: bool = ...,
+ fillvalue: None = None
+) -> Iterator[Tuple[Optional[_T1], Optional[_T2]]]: ...
+@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[Optional[_T], ...]]: ...
+@overload
+def zip_offset(
+ __iter1: Iterable[_T1],
+ *,
+ offsets: _SizedIterable[int],
+ longest: bool = ...,
+ fillvalue: _U,
+) -> Iterator[Tuple[Union[_T1, _U]]]: ...
+@overload
+def zip_offset(
+ __iter1: Iterable[_T1],
+ __iter2: Iterable[_T2],
+ *,
+ offsets: _SizedIterable[int],
+ longest: bool = ...,
+ fillvalue: _U,
+) -> Iterator[Tuple[Union[_T1, _U], Union[_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[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]]: ...
+@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: 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 map_if(
+ iterable: Iterable[Any],
+ pred: Callable[[Any], bool],
+ func: Callable[[Any], Any],
+ func_else: Optional[Callable[[Any], Any]] = ...,
+) -> Iterator[Any]: ...
+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,
+ 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: 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: ...
+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: Union[_T, Iterable[_T]],
+ scalar_types: Union[
+ type, Tuple[Union[type, Tuple[Any, ...]], ...], None
+ ] = ...,
+ strict: bool = ...
+) -> Iterable[Tuple[_T, ...]]: ...
+def unique_in_window(
+ iterable: Iterable[_T], n: int, key: Optional[Callable[[_T], _U]] = ...
+) -> Iterator[_T]: ...
+def duplicates_everseen(
+ iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ...
+) -> Iterator[_T]: ...
+def duplicates_justseen(
+ iterable: Iterable[_T], key: Optional[Callable[[_T], _U]] = ...
+) -> Iterator[_T]: ...
+
+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
+) -> Union[_U, Tuple[_SupportsLessThanT, _SupportsLessThanT]]: ...
+@overload
+def minmax(
+ iterable_or_value: Iterable[_T],
+ *,
+ key: Callable[[_T], _SupportsLessThan],
+ default: _U,
+) -> Union[_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]: ...
diff --git a/pkg_resources/_vendor/more_itertools/py.typed b/pkg_resources/_vendor/more_itertools/py.typed
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools/py.typed
diff --git a/pkg_resources/_vendor/more_itertools/recipes.py b/pkg_resources/_vendor/more_itertools/recipes.py
new file mode 100644
index 00000000..a2596423
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools/recipes.py
@@ -0,0 +1,698 @@
+"""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',
+ 'before_and_after',
+ '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',
+ 'sliding_window',
+ 'tabulate',
+ 'tail',
+ 'take',
+ 'triplewise',
+ '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 "<stdin>", line 1, in <module>
+ 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 "<stdin>", line 1, in <module>
+ 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]
+
+ 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.
+
+ """
+ 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))
+
+
+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
+
+ def remainder_iterator():
+ yield from transition
+ yield from 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), maxlen=n)
+ if len(window) == n:
+ yield tuple(window)
+ for x in it:
+ window.append(x)
+ yield tuple(window)
diff --git a/pkg_resources/_vendor/more_itertools/recipes.pyi b/pkg_resources/_vendor/more_itertools/recipes.pyi
new file mode 100644
index 00000000..4648a41b
--- /dev/null
+++ b/pkg_resources/_vendor/more_itertools/recipes.pyi
@@ -0,0 +1,112 @@
+"""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: Union[Type[BaseException], Tuple[Type[BaseException], ...]],
+ first: None = ...,
+) -> Iterator[_T]: ...
+@overload
+def iter_except(
+ func: Callable[[], _T],
+ exception: Union[Type[BaseException], Tuple[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]: ...
+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, ...]]: ...
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/INSTALLER b/pkg_resources/_vendor/packaging-21.2.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE b/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE
new file mode 100644
index 00000000..6f62d44e
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE
@@ -0,0 +1,3 @@
+This software is made available under the terms of *either* of the licenses
+found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made
+under the terms of *both* these licenses.
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.APACHE b/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.APACHE
new file mode 100644
index 00000000..f433b1a5
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.APACHE
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.BSD b/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.BSD
new file mode 100644
index 00000000..42ce7b75
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/LICENSE.BSD
@@ -0,0 +1,23 @@
+Copyright (c) Donald Stufft and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/METADATA b/pkg_resources/_vendor/packaging-21.2.dist-info/METADATA
new file mode 100644
index 00000000..e8ff54d7
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/METADATA
@@ -0,0 +1,446 @@
+Metadata-Version: 2.1
+Name: packaging
+Version: 21.2
+Summary: Core utilities for Python packages
+Home-page: https://github.com/pypa/packaging
+Author: Donald Stufft and individual contributors
+Author-email: donald@stufft.io
+License: BSD-2-Clause or Apache-2.0
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+License-File: LICENSE.APACHE
+License-File: LICENSE.BSD
+Requires-Dist: pyparsing (<3,>=2.0.2)
+
+packaging
+=========
+
+.. start-intro
+
+Reusable core utilities for various Python Packaging
+`interoperability specifications <https://packaging.python.org/specifications/>`_.
+
+This library provides utilities that implement the interoperability
+specifications which have clearly one correct behaviour (eg: :pep:`440`)
+or benefit greatly from having a single shared implementation (eg: :pep:`425`).
+
+.. end-intro
+
+The ``packaging`` project includes the following: version handling, specifiers,
+markers, requirements, tags, utilities.
+
+Documentation
+-------------
+
+The `documentation`_ provides information and the API for the following:
+
+- Version Handling
+- Specifiers
+- Markers
+- Requirements
+- Tags
+- Utilities
+
+Installation
+------------
+
+Use ``pip`` to install these utilities::
+
+ pip install packaging
+
+Discussion
+----------
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+You can also join ``#pypa`` on Freenode to ask questions or get involved.
+
+
+.. _`documentation`: https://packaging.pypa.io/
+.. _`issue tracker`: https://github.com/pypa/packaging/issues
+
+
+Code of Conduct
+---------------
+
+Everyone interacting in the packaging project's codebases, issue trackers, chat
+rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
+
+.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
+
+Contributing
+------------
+
+The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as
+well as how to report a potential security issue. The documentation for this
+project also covers information about `project development`_ and `security`_.
+
+.. _`project development`: https://packaging.pypa.io/en/latest/development/
+.. _`security`: https://packaging.pypa.io/en/latest/security/
+
+Project History
+---------------
+
+Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for
+recent changes and project history.
+
+.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/
+
+Changelog
+---------
+
+21.2 - 2021-10-29
+~~~~~~~~~~~~~~~~~
+
+* Update documentation entry for 21.1.
+
+21.1 - 2021-10-29
+~~~~~~~~~~~~~~~~~
+
+* Update pin to pyparsing to exclude 3.0.0.
+
+21.0 - 2021-07-03
+~~~~~~~~~~~~~~~~~
+
+* PEP 656: musllinux support (`#411 <https://github.com/pypa/packaging/issues/411>`__)
+* Drop support for Python 2.7, Python 3.4 and Python 3.5.
+* Replace distutils usage with sysconfig (`#396 <https://github.com/pypa/packaging/issues/396>`__)
+* Add support for zip files in ``parse_sdist_filename`` (`#429 <https://github.com/pypa/packaging/issues/429>`__)
+* Use cached ``_hash`` attribute to short-circuit tag equality comparisons (`#417 <https://github.com/pypa/packaging/issues/417>`__)
+* Specify the default value for the ``specifier`` argument to ``SpecifierSet`` (`#437 <https://github.com/pypa/packaging/issues/437>`__)
+* Proper keyword-only "warn" argument in packaging.tags (`#403 <https://github.com/pypa/packaging/issues/403>`__)
+* Correctly remove prerelease suffixes from ~= check (`#366 <https://github.com/pypa/packaging/issues/366>`__)
+* Fix type hints for ``Version.post`` and ``Version.dev`` (`#393 <https://github.com/pypa/packaging/issues/393>`__)
+* Use typing alias ``UnparsedVersion`` (`#398 <https://github.com/pypa/packaging/issues/398>`__)
+* Improve type inference for ``packaging.specifiers.filter()`` (`#430 <https://github.com/pypa/packaging/issues/430>`__)
+* Tighten the return type of ``canonicalize_version()`` (`#402 <https://github.com/pypa/packaging/issues/402>`__)
+
+20.9 - 2021-01-29
+~~~~~~~~~~~~~~~~~
+
+* Run `isort <https://pypi.org/project/isort/>`_ over the code base (`#377 <https://github.com/pypa/packaging/issues/377>`__)
+* Add support for the ``macosx_10_*_universal2`` platform tags (`#379 <https://github.com/pypa/packaging/issues/379>`__)
+* Introduce ``packaging.utils.parse_wheel_filename()`` and ``parse_sdist_filename()``
+ (`#387 <https://github.com/pypa/packaging/issues/387>`__ and `#389 <https://github.com/pypa/packaging/issues/389>`__)
+
+20.8 - 2020-12-11
+~~~~~~~~~~~~~~~~~
+
+* Revert back to setuptools for compatibility purposes for some Linux distros (`#363 <https://github.com/pypa/packaging/issues/363>`__)
+* Do not insert an underscore in wheel tags when the interpreter version number
+ is more than 2 digits (`#372 <https://github.com/pypa/packaging/issues/372>`__)
+
+20.7 - 2020-11-28
+~~~~~~~~~~~~~~~~~
+
+No unreleased changes.
+
+20.6 - 2020-11-28
+~~~~~~~~~~~~~~~~~
+
+.. note:: This release was subsequently yanked, and these changes were included in 20.7.
+
+* Fix flit configuration, to include LICENSE files (`#357 <https://github.com/pypa/packaging/issues/357>`__)
+* Make `intel` a recognized CPU architecture for the `universal` macOS platform tag (`#361 <https://github.com/pypa/packaging/issues/361>`__)
+* Add some missing type hints to `packaging.requirements` (issue:`350`)
+
+20.5 - 2020-11-27
+~~~~~~~~~~~~~~~~~
+
+* Officially support Python 3.9 (`#343 <https://github.com/pypa/packaging/issues/343>`__)
+* Deprecate the ``LegacyVersion`` and ``LegacySpecifier`` classes (`#321 <https://github.com/pypa/packaging/issues/321>`__)
+* Handle ``OSError`` on non-dynamic executables when attempting to resolve
+ the glibc version string.
+
+20.4 - 2020-05-19
+~~~~~~~~~~~~~~~~~
+
+* Canonicalize version before comparing specifiers. (`#282 <https://github.com/pypa/packaging/issues/282>`__)
+* Change type hint for ``canonicalize_name`` to return
+ ``packaging.utils.NormalizedName``.
+ This enables the use of static typing tools (like mypy) to detect mixing of
+ normalized and un-normalized names.
+
+20.3 - 2020-03-05
+~~~~~~~~~~~~~~~~~
+
+* Fix changelog for 20.2.
+
+20.2 - 2020-03-05
+~~~~~~~~~~~~~~~~~
+
+* Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8,
+ aarch64), to report the wrong bitness.
+
+20.1 - 2020-01-24
+~~~~~~~~~~~~~~~~~~~
+
+* Fix a bug caused by reuse of an exhausted iterator. (`#257 <https://github.com/pypa/packaging/issues/257>`__)
+
+20.0 - 2020-01-06
+~~~~~~~~~~~~~~~~~
+
+* Add type hints (`#191 <https://github.com/pypa/packaging/issues/191>`__)
+
+* Add proper trove classifiers for PyPy support (`#198 <https://github.com/pypa/packaging/issues/198>`__)
+
+* Scale back depending on ``ctypes`` for manylinux support detection (`#171 <https://github.com/pypa/packaging/issues/171>`__)
+
+* Use ``sys.implementation.name`` where appropriate for ``packaging.tags`` (`#193 <https://github.com/pypa/packaging/issues/193>`__)
+
+* Expand upon the API provided by ``packaging.tags``: ``interpreter_name()``, ``mac_platforms()``, ``compatible_tags()``, ``cpython_tags()``, ``generic_tags()`` (`#187 <https://github.com/pypa/packaging/issues/187>`__)
+
+* Officially support Python 3.8 (`#232 <https://github.com/pypa/packaging/issues/232>`__)
+
+* Add ``major``, ``minor``, and ``micro`` aliases to ``packaging.version.Version`` (`#226 <https://github.com/pypa/packaging/issues/226>`__)
+
+* Properly mark ``packaging`` has being fully typed by adding a `py.typed` file (`#226 <https://github.com/pypa/packaging/issues/226>`__)
+
+19.2 - 2019-09-18
+~~~~~~~~~~~~~~~~~
+
+* Remove dependency on ``attrs`` (`#178 <https://github.com/pypa/packaging/issues/178>`__, `#179 <https://github.com/pypa/packaging/issues/179>`__)
+
+* Use appropriate fallbacks for CPython ABI tag (`#181 <https://github.com/pypa/packaging/issues/181>`__, `#185 <https://github.com/pypa/packaging/issues/185>`__)
+
+* Add manylinux2014 support (`#186 <https://github.com/pypa/packaging/issues/186>`__)
+
+* Improve ABI detection (`#181 <https://github.com/pypa/packaging/issues/181>`__)
+
+* Properly handle debug wheels for Python 3.8 (`#172 <https://github.com/pypa/packaging/issues/172>`__)
+
+* Improve detection of debug builds on Windows (`#194 <https://github.com/pypa/packaging/issues/194>`__)
+
+19.1 - 2019-07-30
+~~~~~~~~~~~~~~~~~
+
+* Add the ``packaging.tags`` module. (`#156 <https://github.com/pypa/packaging/issues/156>`__)
+
+* Correctly handle two-digit versions in ``python_version`` (`#119 <https://github.com/pypa/packaging/issues/119>`__)
+
+
+19.0 - 2019-01-20
+~~~~~~~~~~~~~~~~~
+
+* Fix string representation of PEP 508 direct URL requirements with markers.
+
+* Better handling of file URLs
+
+ This allows for using ``file:///absolute/path``, which was previously
+ prevented due to the missing ``netloc``.
+
+ This allows for all file URLs that ``urlunparse`` turns back into the
+ original URL to be valid.
+
+
+18.0 - 2018-09-26
+~~~~~~~~~~~~~~~~~
+
+* Improve error messages when invalid requirements are given. (`#129 <https://github.com/pypa/packaging/issues/129>`__)
+
+
+17.1 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions.
+
+
+17.0 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Drop support for python 2.6, 3.2, and 3.3.
+
+* Define minimal pyparsing version to 2.0.2 (`#91 <https://github.com/pypa/packaging/issues/91>`__).
+
+* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to
+ ``Version`` and ``LegacyVersion`` (`#34 <https://github.com/pypa/packaging/issues/34>`__).
+
+* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to
+ make it easy to determine if a release is a development release.
+
+* Add ``utils.canonicalize_version`` to canonicalize version strings or
+ ``Version`` instances (`#121 <https://github.com/pypa/packaging/issues/121>`__).
+
+
+16.8 - 2016-10-29
+~~~~~~~~~~~~~~~~~
+
+* Fix markers that utilize ``in`` so that they render correctly.
+
+* Fix an erroneous test on Python RC releases.
+
+
+16.7 - 2016-04-23
+~~~~~~~~~~~~~~~~~
+
+* Add support for the deprecated ``python_implementation`` marker which was
+ an undocumented setuptools marker in addition to the newer markers.
+
+
+16.6 - 2016-03-29
+~~~~~~~~~~~~~~~~~
+
+* Add support for the deprecated, PEP 345 environment markers in addition to
+ the newer markers.
+
+
+16.5 - 2016-02-26
+~~~~~~~~~~~~~~~~~
+
+* Fix a regression in parsing requirements with whitespaces between the comma
+ separators.
+
+
+16.4 - 2016-02-22
+~~~~~~~~~~~~~~~~~
+
+* Fix a regression in parsing requirements like ``foo (==4)``.
+
+
+16.3 - 2016-02-21
+~~~~~~~~~~~~~~~~~
+
+* Fix a bug where ``packaging.requirements:Requirement`` was overly strict when
+ matching legacy requirements.
+
+
+16.2 - 2016-02-09
+~~~~~~~~~~~~~~~~~
+
+* Add a function that implements the name canonicalization from PEP 503.
+
+
+16.1 - 2016-02-07
+~~~~~~~~~~~~~~~~~
+
+* Implement requirement specifiers from PEP 508.
+
+
+16.0 - 2016-01-19
+~~~~~~~~~~~~~~~~~
+
+* Relicense so that packaging is available under *either* the Apache License,
+ Version 2.0 or a 2 Clause BSD license.
+
+* Support installation of packaging when only distutils is available.
+
+* Fix ``==`` comparison when there is a prefix and a local version in play.
+ (`#41 <https://github.com/pypa/packaging/issues/41>`__).
+
+* Implement environment markers from PEP 508.
+
+
+15.3 - 2015-08-01
+~~~~~~~~~~~~~~~~~
+
+* Normalize post-release spellings for rev/r prefixes. `#35 <https://github.com/pypa/packaging/issues/35>`__
+
+
+15.2 - 2015-05-13
+~~~~~~~~~~~~~~~~~
+
+* Fix an error where the arbitrary specifier (``===``) was not correctly
+ allowing pre-releases when it was being used.
+
+* Expose the specifier and version parts through properties on the
+ ``Specifier`` classes.
+
+* Allow iterating over the ``SpecifierSet`` to get access to all of the
+ ``Specifier`` instances.
+
+* Allow testing if a version is contained within a specifier via the ``in``
+ operator.
+
+
+15.1 - 2015-04-13
+~~~~~~~~~~~~~~~~~
+
+* Fix a logic error that was causing inconsistent answers about whether or not
+ a pre-release was contained within a ``SpecifierSet`` or not.
+
+
+15.0 - 2015-01-02
+~~~~~~~~~~~~~~~~~
+
+* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to
+ make it easy to determine if a release is a post release.
+
+* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make
+ it easy to get the public version without any pre or post release markers.
+
+* Support the update to PEP 440 which removed the implied ``!=V.*`` when using
+ either ``>V`` or ``<V`` and which instead special cased the handling of
+ pre-releases, post-releases, and local versions when using ``>V`` or ``<V``.
+
+
+14.5 - 2014-12-17
+~~~~~~~~~~~~~~~~~
+
+* Normalize release candidates as ``rc`` instead of ``c``.
+
+* Expose the ``VERSION_PATTERN`` constant, a regular expression matching
+ a valid version.
+
+
+14.4 - 2014-12-15
+~~~~~~~~~~~~~~~~~
+
+* Ensure that versions are normalized before comparison when used in a
+ specifier with a less than (``<``) or greater than (``>``) operator.
+
+
+14.3 - 2014-11-19
+~~~~~~~~~~~~~~~~~
+
+* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely
+ handle legacy specifiers as well as PEP 440 specifiers.
+
+* **BACKWARDS INCOMPATIBLE** Move the specifier support out of
+ ``packaging.version`` into ``packaging.specifiers``.
+
+
+14.2 - 2014-09-10
+~~~~~~~~~~~~~~~~~
+
+* Add prerelease support to ``Specifier``.
+* Remove the ability to do ``item in Specifier()`` and replace it with
+ ``Specifier().contains(item)`` in order to allow flags that signal if a
+ prerelease should be accepted or not.
+* Add a method ``Specifier().filter()`` which will take an iterable and returns
+ an iterable with items that do not match the specifier filtered out.
+
+
+14.1 - 2014-09-08
+~~~~~~~~~~~~~~~~~
+
+* Allow ``LegacyVersion`` and ``Version`` to be sorted together.
+* Add ``packaging.version.parse()`` to enable easily parsing a version string
+ as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440
+ validity.
+
+
+14.0 - 2014-09-05
+~~~~~~~~~~~~~~~~~
+
+* Initial release.
+
+
+.. _`master`: https://github.com/pypa/packaging/
+
+
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/RECORD b/pkg_resources/_vendor/packaging-21.2.dist-info/RECORD
new file mode 100644
index 00000000..ed2291ac
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/RECORD
@@ -0,0 +1,32 @@
+packaging-21.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+packaging-21.2.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+packaging-21.2.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+packaging-21.2.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+packaging-21.2.dist-info/METADATA,sha256=N4A8uSYrQwV9byem7YuI9OtVkbqiNzFlDhcDVT-suAo,14754
+packaging-21.2.dist-info/RECORD,,
+packaging-21.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+packaging-21.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+packaging-21.2.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10
+packaging/__about__.py,sha256=IIRHpOsJlJSgkjq1UoeBoMTqhvNp3gN9FyMb5Kf8El4,661
+packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497
+packaging/__pycache__/__about__.cpython-310.pyc,,
+packaging/__pycache__/__init__.cpython-310.pyc,,
+packaging/__pycache__/_manylinux.cpython-310.pyc,,
+packaging/__pycache__/_musllinux.cpython-310.pyc,,
+packaging/__pycache__/_structures.cpython-310.pyc,,
+packaging/__pycache__/markers.cpython-310.pyc,,
+packaging/__pycache__/requirements.cpython-310.pyc,,
+packaging/__pycache__/specifiers.cpython-310.pyc,,
+packaging/__pycache__/tags.cpython-310.pyc,,
+packaging/__pycache__/utils.cpython-310.pyc,,
+packaging/__pycache__/version.cpython-310.pyc,,
+packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488
+packaging/_musllinux.py,sha256=z5yeG1ygOPx4uUyLdqj-p8Dk5UBb5H_b0NIjW9yo8oA,4378
+packaging/_structures.py,sha256=TMiAgFbdUOPmIfDIfiHc3KFhSJ8kMjof2QS5I-2NyQ8,1629
+packaging/markers.py,sha256=Fygi3_eZnjQ-3VJizW5AhI5wvo0Hb6RMk4DidsKpOC0,8475
+packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+packaging/requirements.py,sha256=rjaGRCMepZS1mlYMjJ5Qh6rfq3gtsCRQUQmftGZ_bu8,4664
+packaging/specifiers.py,sha256=MZ-fYcNL3u7pNrt-6g2EQO7AbRXkjc-SPEYwXMQbLmc,30964
+packaging/tags.py,sha256=vGybAUQYlPKMcukzX_2e65fmafnFFuMbD25naYTEwtc,15710
+packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200
+packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/REQUESTED b/pkg_resources/_vendor/packaging-21.2.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/REQUESTED
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/WHEEL b/pkg_resources/_vendor/packaging-21.2.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/packaging-21.2.dist-info/top_level.txt b/pkg_resources/_vendor/packaging-21.2.dist-info/top_level.txt
new file mode 100644
index 00000000..748809f7
--- /dev/null
+++ b/pkg_resources/_vendor/packaging-21.2.dist-info/top_level.txt
@@ -0,0 +1 @@
+packaging
diff --git a/pkg_resources/_vendor/packaging/__about__.py b/pkg_resources/_vendor/packaging/__about__.py
index 4d998578..c359122f 100644
--- a/pkg_resources/_vendor/packaging/__about__.py
+++ b/pkg_resources/_vendor/packaging/__about__.py
@@ -1,7 +1,6 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
__all__ = [
"__title__",
@@ -18,10 +17,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
-__version__ = "20.4"
+__version__ = "21.2"
__author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io"
__license__ = "BSD-2-Clause or Apache-2.0"
-__copyright__ = "Copyright 2014-2019 %s" % __author__
+__copyright__ = "2014-2019 %s" % __author__
diff --git a/pkg_resources/_vendor/packaging/__init__.py b/pkg_resources/_vendor/packaging/__init__.py
index a0cf67df..3c50c5dc 100644
--- a/pkg_resources/_vendor/packaging/__init__.py
+++ b/pkg_resources/_vendor/packaging/__init__.py
@@ -1,7 +1,6 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
from .__about__ import (
__author__,
diff --git a/pkg_resources/_vendor/packaging/_compat.py b/pkg_resources/_vendor/packaging/_compat.py
deleted file mode 100644
index e54bd4ed..00000000
--- a/pkg_resources/_vendor/packaging/_compat.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# This file is dual licensed under the terms of the Apache License, Version
-# 2.0, and the BSD License. See the LICENSE file in the root of this repository
-# for complete details.
-from __future__ import absolute_import, division, print_function
-
-import sys
-
-from ._typing import TYPE_CHECKING
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Any, Dict, Tuple, Type
-
-
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-# flake8: noqa
-
-if PY3:
- string_types = (str,)
-else:
- string_types = (basestring,)
-
-
-def with_metaclass(meta, *bases):
- # type: (Type[Any], Tuple[Type[Any], ...]) -> Any
- """
- Create a base class with a metaclass.
- """
- # This requires a bit of explanation: the basic idea is to make a dummy
- # metaclass for one level of class instantiation that replaces itself with
- # the actual metaclass.
- class metaclass(meta): # type: ignore
- def __new__(cls, name, this_bases, d):
- # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
- return meta(name, bases, d)
-
- return type.__new__(metaclass, "temporary_class", (), {})
diff --git a/pkg_resources/_vendor/packaging/_manylinux.py b/pkg_resources/_vendor/packaging/_manylinux.py
new file mode 100644
index 00000000..4c379aa6
--- /dev/null
+++ b/pkg_resources/_vendor/packaging/_manylinux.py
@@ -0,0 +1,301 @@
+import collections
+import functools
+import os
+import re
+import struct
+import sys
+import warnings
+from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple
+
+
+# Python does not provide platform information at sufficient granularity to
+# identify the architecture of the running executable in some cases, so we
+# determine it dynamically by reading the information from the running
+# process. This only applies on Linux, which uses the ELF format.
+class _ELFFileHeader:
+ # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
+ class _InvalidELFFileHeader(ValueError):
+ """
+ An invalid ELF file header was found.
+ """
+
+ ELF_MAGIC_NUMBER = 0x7F454C46
+ ELFCLASS32 = 1
+ ELFCLASS64 = 2
+ ELFDATA2LSB = 1
+ ELFDATA2MSB = 2
+ EM_386 = 3
+ EM_S390 = 22
+ EM_ARM = 40
+ EM_X86_64 = 62
+ EF_ARM_ABIMASK = 0xFF000000
+ EF_ARM_ABI_VER5 = 0x05000000
+ EF_ARM_ABI_FLOAT_HARD = 0x00000400
+
+ def __init__(self, file: IO[bytes]) -> None:
+ def unpack(fmt: str) -> int:
+ try:
+ data = file.read(struct.calcsize(fmt))
+ result: Tuple[int, ...] = struct.unpack(fmt, data)
+ except struct.error:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ return result[0]
+
+ self.e_ident_magic = unpack(">I")
+ if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_class = unpack("B")
+ if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_data = unpack("B")
+ if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_version = unpack("B")
+ self.e_ident_osabi = unpack("B")
+ self.e_ident_abiversion = unpack("B")
+ self.e_ident_pad = file.read(7)
+ format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
+ format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
+ format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
+ format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
+ self.e_type = unpack(format_h)
+ self.e_machine = unpack(format_h)
+ self.e_version = unpack(format_i)
+ self.e_entry = unpack(format_p)
+ self.e_phoff = unpack(format_p)
+ self.e_shoff = unpack(format_p)
+ self.e_flags = unpack(format_i)
+ self.e_ehsize = unpack(format_h)
+ self.e_phentsize = unpack(format_h)
+ self.e_phnum = unpack(format_h)
+ self.e_shentsize = unpack(format_h)
+ self.e_shnum = unpack(format_h)
+ self.e_shstrndx = unpack(format_h)
+
+
+def _get_elf_header() -> Optional[_ELFFileHeader]:
+ try:
+ with open(sys.executable, "rb") as f:
+ elf_header = _ELFFileHeader(f)
+ except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
+ return None
+ return elf_header
+
+
+def _is_linux_armhf() -> bool:
+ # hard-float ABI can be detected from the ELF header of the running
+ # process
+ # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
+ elf_header = _get_elf_header()
+ if elf_header is None:
+ return False
+ result = elf_header.e_ident_class == elf_header.ELFCLASS32
+ result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
+ result &= elf_header.e_machine == elf_header.EM_ARM
+ result &= (
+ elf_header.e_flags & elf_header.EF_ARM_ABIMASK
+ ) == elf_header.EF_ARM_ABI_VER5
+ result &= (
+ elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD
+ ) == elf_header.EF_ARM_ABI_FLOAT_HARD
+ return result
+
+
+def _is_linux_i686() -> bool:
+ elf_header = _get_elf_header()
+ if elf_header is None:
+ return False
+ result = elf_header.e_ident_class == elf_header.ELFCLASS32
+ result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
+ result &= elf_header.e_machine == elf_header.EM_386
+ return result
+
+
+def _have_compatible_abi(arch: str) -> bool:
+ if arch == "armv7l":
+ return _is_linux_armhf()
+ if arch == "i686":
+ return _is_linux_i686()
+ return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"}
+
+
+# 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:
+ # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
+ version_string = os.confstr("CS_GNU_LIBC_VERSION")
+ assert version_string is not None
+ _, version = version_string.split()
+ 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<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
+ if not m:
+ warnings.warn(
+ "Expected glibc version with 2 components major.minor,"
+ " got: %s" % version_str,
+ RuntimeWarning,
+ )
+ return -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(name: str, 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 # noqa
+ 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(linux: str, arch: str) -> Iterator[str]:
+ if not _have_compatible_abi(arch):
+ return
+ # Oldest glibc to be supported regardless of architecture is (2, 17).
+ too_old_glibc2 = _GLibCVersion(2, 16)
+ if arch in {"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 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(tag, arch, glibc_version):
+ yield linux.replace("linux", tag)
+ # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags.
+ if glibc_version in _LEGACY_MANYLINUX_MAP:
+ legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version]
+ if _is_compatible(legacy_tag, arch, glibc_version):
+ yield linux.replace("linux", legacy_tag)
diff --git a/pkg_resources/_vendor/packaging/_musllinux.py b/pkg_resources/_vendor/packaging/_musllinux.py
new file mode 100644
index 00000000..85450faf
--- /dev/null
+++ b/pkg_resources/_vendor/packaging/_musllinux.py
@@ -0,0 +1,136 @@
+"""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 contextlib
+import functools
+import operator
+import os
+import re
+import struct
+import subprocess
+import sys
+from typing import IO, Iterator, NamedTuple, Optional, Tuple
+
+
+def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]:
+ return struct.unpack(fmt, f.read(struct.calcsize(fmt)))
+
+
+def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]:
+ """Detect musl libc location by parsing the Python executable.
+
+ Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca
+ ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
+ """
+ f.seek(0)
+ try:
+ ident = _read_unpacked(f, "16B")
+ except struct.error:
+ return None
+ if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF.
+ return None
+ f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version.
+
+ 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, p_fmt, p_idx = {
+ 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit.
+ 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit.
+ }[ident[4]]
+ except KeyError:
+ return None
+ else:
+ p_get = operator.itemgetter(*p_idx)
+
+ # Find the interpreter section and return its content.
+ try:
+ _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt)
+ except struct.error:
+ return None
+ for i in range(e_phnum + 1):
+ f.seek(e_phoff + e_phentsize * i)
+ try:
+ p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt))
+ except struct.error:
+ return None
+ if p_type != 3: # Not PT_INTERP.
+ continue
+ f.seek(p_offset)
+ interpreter = os.fsdecode(f.read(p_filesz)).strip("\0")
+ if "musl" not in interpreter:
+ return None
+ return interpreter
+ return None
+
+
+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
+ """
+ with contextlib.ExitStack() as stack:
+ try:
+ f = stack.enter_context(open(executable, "rb"))
+ except IOError:
+ return None
+ ld = _parse_ld_musl_from_elf(f)
+ if not ld:
+ return None
+ proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True)
+ return _parse_musl_version(proc.stderr)
+
+
+def platform_tags(arch: str) -> Iterator[str]:
+ """Generate musllinux tags compatible to the current platform.
+
+ :param arch: Should 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 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/pkg_resources/_vendor/packaging/_structures.py b/pkg_resources/_vendor/packaging/_structures.py
index 800d5c55..95154975 100644
--- a/pkg_resources/_vendor/packaging/_structures.py
+++ b/pkg_resources/_vendor/packaging/_structures.py
@@ -1,85 +1,66 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
-class InfinityType(object):
- def __repr__(self):
- # type: () -> str
+class InfinityType:
+ def __repr__(self) -> str:
return "Infinity"
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(repr(self))
- def __lt__(self, other):
- # type: (object) -> bool
+ def __lt__(self, other: object) -> bool:
return False
- def __le__(self, other):
- # type: (object) -> bool
+ def __le__(self, other: object) -> bool:
return False
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
return isinstance(other, self.__class__)
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
return not isinstance(other, self.__class__)
- def __gt__(self, other):
- # type: (object) -> bool
+ def __gt__(self, other: object) -> bool:
return True
- def __ge__(self, other):
- # type: (object) -> bool
+ def __ge__(self, other: object) -> bool:
return True
- def __neg__(self):
- # type: (object) -> NegativeInfinityType
+ def __neg__(self: object) -> "NegativeInfinityType":
return NegativeInfinity
Infinity = InfinityType()
-class NegativeInfinityType(object):
- def __repr__(self):
- # type: () -> str
+class NegativeInfinityType:
+ def __repr__(self) -> str:
return "-Infinity"
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(repr(self))
- def __lt__(self, other):
- # type: (object) -> bool
+ def __lt__(self, other: object) -> bool:
return True
- def __le__(self, other):
- # type: (object) -> bool
+ def __le__(self, other: object) -> bool:
return True
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
return isinstance(other, self.__class__)
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
return not isinstance(other, self.__class__)
- def __gt__(self, other):
- # type: (object) -> bool
+ def __gt__(self, other: object) -> bool:
return False
- def __ge__(self, other):
- # type: (object) -> bool
+ def __ge__(self, other: object) -> bool:
return False
- def __neg__(self):
- # type: (object) -> InfinityType
+ def __neg__(self: object) -> InfinityType:
return Infinity
diff --git a/pkg_resources/_vendor/packaging/_typing.py b/pkg_resources/_vendor/packaging/_typing.py
deleted file mode 100644
index 77a8b918..00000000
--- a/pkg_resources/_vendor/packaging/_typing.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""For neatly implementing static typing in packaging.
-
-`mypy` - the static type analysis tool we use - uses the `typing` module, which
-provides core functionality fundamental to mypy's functioning.
-
-Generally, `typing` would be imported at runtime and used in that fashion -
-it acts as a no-op at runtime and does not have any run-time overhead by
-design.
-
-As it turns out, `typing` is not vendorable - it uses separate sources for
-Python 2/Python 3. Thus, this codebase can not expect it to be present.
-To work around this, mypy allows the typing import to be behind a False-y
-optional to prevent it from running at runtime and type-comments can be used
-to remove the need for the types to be accessible directly during runtime.
-
-This module provides the False-y guard in a nicely named fashion so that a
-curious maintainer can reach here to read this.
-
-In packaging, all static-typing related imports should be guarded as follows:
-
- from packaging._typing import TYPE_CHECKING
-
- if TYPE_CHECKING:
- from typing import ...
-
-Ref: https://github.com/python/mypy/issues/3216
-"""
-
-__all__ = ["TYPE_CHECKING", "cast"]
-
-# The TYPE_CHECKING constant defined by the typing module is False at runtime
-# but True while type checking.
-if False: # pragma: no cover
- from typing import TYPE_CHECKING
-else:
- TYPE_CHECKING = False
-
-# typing's cast syntax requires calling typing.cast at runtime, but we don't
-# want to import typing at runtime. Here, we inform the type checkers that
-# we're importing `typing.cast` as `cast` and re-implement typing.cast's
-# runtime behavior in a block that is ignored by type checkers.
-if TYPE_CHECKING: # pragma: no cover
- # not executed at runtime
- from typing import cast
-else:
- # executed at runtime
- def cast(type_, value): # noqa
- return value
diff --git a/pkg_resources/_vendor/packaging/markers.py b/pkg_resources/_vendor/packaging/markers.py
index fd1559c1..18769b09 100644
--- a/pkg_resources/_vendor/packaging/markers.py
+++ b/pkg_resources/_vendor/packaging/markers.py
@@ -1,26 +1,26 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import operator
import os
import platform
import sys
+from typing import Any, Callable, Dict, List, Optional, Tuple, Union
+
+from pkg_resources.extern.pyparsing import ( # noqa: N817
+ Forward,
+ Group,
+ Literal as L,
+ ParseException,
+ ParseResults,
+ QuotedString,
+ ZeroOrMore,
+ stringEnd,
+ stringStart,
+)
-from pkg_resources.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd
-from pkg_resources.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString
-from pkg_resources.extern.pyparsing import Literal as L # noqa
-
-from ._compat import string_types
-from ._typing import TYPE_CHECKING
-from .specifiers import Specifier, InvalidSpecifier
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
-
- Operator = Callable[[str, str], bool]
-
+from .specifiers import InvalidSpecifier, Specifier
__all__ = [
"InvalidMarker",
@@ -30,6 +30,8 @@ __all__ = [
"default_environment",
]
+Operator = Callable[[str, str], bool]
+
class InvalidMarker(ValueError):
"""
@@ -50,39 +52,32 @@ class UndefinedEnvironmentName(ValueError):
"""
-class Node(object):
- def __init__(self, value):
- # type: (Any) -> None
+class Node:
+ def __init__(self, value: Any) -> None:
self.value = value
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return str(self.value)
- def __repr__(self):
- # type: () -> str
- return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
+ def __repr__(self) -> str:
+ return f"<{self.__class__.__name__}('{self}')>"
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
raise NotImplementedError
class Variable(Node):
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
return str(self)
class Value(Node):
- def serialize(self):
- # type: () -> str
- return '"{0}"'.format(self)
+ def serialize(self) -> str:
+ return f'"{self}"'
class Op(Node):
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
return str(self)
@@ -143,18 +138,18 @@ MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR)
MARKER = stringStart + MARKER_EXPR + stringEnd
-def _coerce_parse_result(results):
- # type: (Union[ParseResults, List[Any]]) -> List[Any]
+def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]:
if isinstance(results, ParseResults):
return [_coerce_parse_result(i) for i in results]
else:
return results
-def _format_marker(marker, first=True):
- # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
+def _format_marker(
+ marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True
+) -> str:
- assert isinstance(marker, (list, tuple, string_types))
+ 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
@@ -179,7 +174,7 @@ def _format_marker(marker, first=True):
return marker
-_operators = {
+_operators: Dict[str, Operator] = {
"in": lambda lhs, rhs: lhs in rhs,
"not in": lambda lhs, rhs: lhs not in rhs,
"<": operator.lt,
@@ -188,11 +183,10 @@ _operators = {
"!=": operator.ne,
">=": operator.ge,
">": operator.gt,
-} # type: Dict[str, Operator]
+}
-def _eval_op(lhs, op, rhs):
- # type: (str, Op, str) -> bool
+def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
try:
spec = Specifier("".join([op.serialize(), rhs]))
except InvalidSpecifier:
@@ -200,40 +194,36 @@ def _eval_op(lhs, op, rhs):
else:
return spec.contains(lhs)
- oper = _operators.get(op.serialize()) # type: Optional[Operator]
+ oper: Optional[Operator] = _operators.get(op.serialize())
if oper is None:
- raise UndefinedComparison(
- "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
- )
+ raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
return oper(lhs, rhs)
-class Undefined(object):
+class Undefined:
pass
_undefined = Undefined()
-def _get_env(environment, name):
- # type: (Dict[str, str], str) -> str
- value = environment.get(name, _undefined) # type: Union[str, Undefined]
+def _get_env(environment: Dict[str, str], name: str) -> str:
+ value: Union[str, Undefined] = environment.get(name, _undefined)
if isinstance(value, Undefined):
raise UndefinedEnvironmentName(
- "{0!r} does not exist in evaluation environment.".format(name)
+ f"{name!r} does not exist in evaluation environment."
)
return value
-def _evaluate_markers(markers, environment):
- # type: (List[Any], Dict[str, str]) -> bool
- groups = [[]] # type: List[List[bool]]
+def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool:
+ groups: List[List[bool]] = [[]]
for marker in markers:
- assert isinstance(marker, (list, tuple, string_types))
+ assert isinstance(marker, (list, tuple, str))
if isinstance(marker, list):
groups[-1].append(_evaluate_markers(marker, environment))
@@ -256,8 +246,7 @@ def _evaluate_markers(markers, environment):
return any(all(item) for item in groups)
-def format_full_version(info):
- # type: (sys._version_info) -> str
+def format_full_version(info: "sys._version_info") -> str:
version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel
if kind != "final":
@@ -265,18 +254,9 @@ def format_full_version(info):
return version
-def default_environment():
- # type: () -> Dict[str, str]
- if hasattr(sys, "implementation"):
- # Ignoring the `sys.implementation` reference for type checking due to
- # mypy not liking that the attribute doesn't exist in Python 2.7 when
- # run with the `--py27` flag.
- iver = format_full_version(sys.implementation.version) # type: ignore
- implementation_name = sys.implementation.name # type: ignore
- else:
- iver = "0"
- implementation_name = ""
-
+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,
@@ -292,27 +272,23 @@ def default_environment():
}
-class Marker(object):
- def __init__(self, marker):
- # type: (str) -> None
+class Marker:
+ def __init__(self, marker: str) -> None:
try:
self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e:
- err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
- marker, marker[e.loc : e.loc + 8]
+ raise InvalidMarker(
+ f"Invalid marker: {marker!r}, parse error at "
+ f"{marker[e.loc : e.loc + 8]!r}"
)
- raise InvalidMarker(err_str)
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return _format_marker(self._markers)
- def __repr__(self):
- # type: () -> str
- return "<Marker({0!r})>".format(str(self))
+ def __repr__(self) -> str:
+ return f"<Marker('{self}')>"
- def evaluate(self, environment=None):
- # type: (Optional[Dict[str, str]]) -> bool
+ def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
"""Evaluate a marker.
Return the boolean from evaluating the given marker against the
diff --git a/pkg_resources/_vendor/packaging/requirements.py b/pkg_resources/_vendor/packaging/requirements.py
index 9495a1df..6af14ec4 100644
--- a/pkg_resources/_vendor/packaging/requirements.py
+++ b/pkg_resources/_vendor/packaging/requirements.py
@@ -1,23 +1,28 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
-import string
import re
+import string
+import urllib.parse
+from typing import List, Optional as TOptional, Set
+
+from pkg_resources.extern.pyparsing import ( # noqa
+ Combine,
+ Literal as L,
+ Optional,
+ ParseException,
+ Regex,
+ Word,
+ ZeroOrMore,
+ originalTextFor,
+ stringEnd,
+ stringStart,
+)
-from pkg_resources.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException
-from pkg_resources.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine
-from pkg_resources.extern.pyparsing import Literal as L # noqa
-from urllib import parse as urlparse
-
-from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
-if TYPE_CHECKING: # pragma: no cover
- from typing import List
-
class InvalidRequirement(ValueError):
"""
@@ -55,7 +60,7 @@ VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
VERSION_MANY = Combine(
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
)("_raw_spec")
-_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
+_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
@@ -79,7 +84,7 @@ REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
REQUIREMENT.parseString("x[]")
-class Requirement(object):
+class Requirement:
"""Parse a requirement.
Parse a given requirement string into its parts, such as name, specifier,
@@ -92,54 +97,50 @@ class Requirement(object):
# the thing as well as the version? What about the markers?
# TODO: Can we normalize the name and extra name?
- def __init__(self, requirement_string):
- # type: (str) -> None
+ def __init__(self, requirement_string: str) -> None:
try:
req = REQUIREMENT.parseString(requirement_string)
except ParseException as e:
raise InvalidRequirement(
- 'Parse error at "{0!r}": {1}'.format(
- requirement_string[e.loc : e.loc + 8], e.msg
- )
+ f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}'
)
- self.name = req.name
+ self.name: str = req.name
if req.url:
- parsed_url = urlparse.urlparse(req.url)
+ parsed_url = urllib.parse.urlparse(req.url)
if parsed_url.scheme == "file":
- if urlparse.urlunparse(parsed_url) != req.url:
+ if urllib.parse.urlunparse(parsed_url) != req.url:
raise InvalidRequirement("Invalid URL given")
elif not (parsed_url.scheme and parsed_url.netloc) or (
not parsed_url.scheme and not parsed_url.netloc
):
- raise InvalidRequirement("Invalid URL: {0}".format(req.url))
- self.url = req.url
+ raise InvalidRequirement(f"Invalid URL: {req.url}")
+ self.url: TOptional[str] = req.url
else:
self.url = None
- self.extras = set(req.extras.asList() if req.extras else [])
- self.specifier = SpecifierSet(req.specifier)
- self.marker = req.marker if req.marker else None
+ self.extras: Set[str] = set(req.extras.asList() if req.extras else [])
+ self.specifier: SpecifierSet = SpecifierSet(req.specifier)
+ self.marker: TOptional[Marker] = req.marker if req.marker else None
- def __str__(self):
- # type: () -> str
- parts = [self.name] # type: List[str]
+ def __str__(self) -> str:
+ parts: List[str] = [self.name]
if self.extras:
- parts.append("[{0}]".format(",".join(sorted(self.extras))))
+ formatted_extras = ",".join(sorted(self.extras))
+ parts.append(f"[{formatted_extras}]")
if self.specifier:
parts.append(str(self.specifier))
if self.url:
- parts.append("@ {0}".format(self.url))
+ parts.append(f"@ {self.url}")
if self.marker:
parts.append(" ")
if self.marker:
- parts.append("; {0}".format(self.marker))
+ parts.append(f"; {self.marker}")
return "".join(parts)
- def __repr__(self):
- # type: () -> str
- return "<Requirement({0!r})>".format(str(self))
+ def __repr__(self) -> str:
+ return f"<Requirement('{self}')>"
diff --git a/pkg_resources/_vendor/packaging/specifiers.py b/pkg_resources/_vendor/packaging/specifiers.py
index fe09bb1d..ce66bd4a 100644
--- a/pkg_resources/_vendor/packaging/specifiers.py
+++ b/pkg_resources/_vendor/packaging/specifiers.py
@@ -1,34 +1,33 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import abc
import functools
import itertools
import re
+import warnings
+from typing import (
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Pattern,
+ Set,
+ Tuple,
+ TypeVar,
+ Union,
+)
-from ._compat import string_types, with_metaclass
-from ._typing import TYPE_CHECKING
from .utils import canonicalize_version
-from .version import Version, LegacyVersion, parse
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import (
- List,
- Dict,
- Union,
- Iterable,
- Iterator,
- Optional,
- Callable,
- Tuple,
- FrozenSet,
- )
+from .version import LegacyVersion, Version, parse
- ParsedVersion = Union[Version, LegacyVersion]
- UnparsedVersion = Union[Version, LegacyVersion, str]
- CallableOperator = Callable[[ParsedVersion, str], bool]
+ParsedVersion = Union[Version, LegacyVersion]
+UnparsedVersion = Union[Version, LegacyVersion, str]
+VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion)
+CallableOperator = Callable[[ParsedVersion, str], bool]
class InvalidSpecifier(ValueError):
@@ -37,64 +36,58 @@ class InvalidSpecifier(ValueError):
"""
-class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore
+class BaseSpecifier(metaclass=abc.ABCMeta):
@abc.abstractmethod
- def __str__(self):
- # type: () -> str
+ 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):
- # type: () -> int
+ def __hash__(self) -> int:
"""
Returns a hash value for this Specifier like object.
"""
@abc.abstractmethod
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
"""
Returns a boolean representing whether or not the two Specifier like
objects are equal.
"""
@abc.abstractmethod
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
"""
Returns a boolean representing whether or not the two Specifier like
objects are not equal.
"""
@abc.abstractproperty
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
"""
Returns whether or not pre-releases as a whole are allowed by this
specifier.
"""
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
"""
Sets whether or not pre-releases as a whole are allowed by this
specifier.
"""
@abc.abstractmethod
- def contains(self, item, prereleases=None):
- # type: (str, Optional[bool]) -> bool
+ 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, prereleases=None):
- # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
+ def filter(
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
"""
Takes an iterable of items and filters them so that only items which
are contained within this specifier are allowed in it.
@@ -103,48 +96,43 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore
class _IndividualSpecifier(BaseSpecifier):
- _operators = {} # type: Dict[str, str]
+ _operators: Dict[str, str] = {}
+ _regex: Pattern[str]
- def __init__(self, spec="", prereleases=None):
- # type: (str, Optional[bool]) -> None
+ def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
match = self._regex.search(spec)
if not match:
- raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
+ raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
- self._spec = (
+ self._spec: Tuple[str, str] = (
match.group("operator").strip(),
match.group("version").strip(),
- ) # type: Tuple[str, str]
+ )
# Store whether or not this Specifier should accept prereleases
self._prereleases = prereleases
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
pre = (
- ", prereleases={0!r}".format(self.prereleases)
+ f", prereleases={self.prereleases!r}"
if self._prereleases is not None
else ""
)
- return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
+ return "<{}({!r}{})>".format(self.__class__.__name__, str(self), pre)
- def __str__(self):
- # type: () -> str
- return "{0}{1}".format(*self._spec)
+ def __str__(self) -> str:
+ return "{}{}".format(*self._spec)
@property
- def _canonical_spec(self):
- # type: () -> Tuple[str, Union[Version, str]]
+ def _canonical_spec(self) -> Tuple[str, str]:
return self._spec[0], canonicalize_version(self._spec[1])
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._canonical_spec)
- def __eq__(self, other):
- # type: (object) -> bool
- if isinstance(other, string_types):
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, str):
try:
other = self.__class__(str(other))
except InvalidSpecifier:
@@ -154,9 +142,8 @@ class _IndividualSpecifier(BaseSpecifier):
return self._canonical_spec == other._canonical_spec
- def __ne__(self, other):
- # type: (object) -> bool
- if isinstance(other, string_types):
+ def __ne__(self, other: object) -> bool:
+ if isinstance(other, str):
try:
other = self.__class__(str(other))
except InvalidSpecifier:
@@ -166,45 +153,39 @@ class _IndividualSpecifier(BaseSpecifier):
return self._spec != other._spec
- def _get_operator(self, op):
- # type: (str) -> CallableOperator
- operator_callable = getattr(
- self, "_compare_{0}".format(self._operators[op])
- ) # type: CallableOperator
+ def _get_operator(self, op: str) -> CallableOperator:
+ operator_callable: CallableOperator = getattr(
+ self, f"_compare_{self._operators[op]}"
+ )
return operator_callable
- def _coerce_version(self, version):
- # type: (UnparsedVersion) -> ParsedVersion
+ def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion:
if not isinstance(version, (LegacyVersion, Version)):
version = parse(version)
return version
@property
- def operator(self):
- # type: () -> str
+ def operator(self) -> str:
return self._spec[0]
@property
- def version(self):
- # type: () -> str
+ def version(self) -> str:
return self._spec[1]
@property
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
return self._prereleases
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
- def __contains__(self, item):
- # type: (str) -> bool
+ def __contains__(self, item: str) -> bool:
return self.contains(item)
- def contains(self, item, prereleases=None):
- # type: (UnparsedVersion, Optional[bool]) -> bool
+ def contains(
+ self, item: UnparsedVersion, prereleases: Optional[bool] = None
+ ) -> bool:
# Determine if prereleases are to be allowed or not.
if prereleases is None:
@@ -222,11 +203,12 @@ class _IndividualSpecifier(BaseSpecifier):
# Actually do the comparison to determine if this item is contained
# within this Specifier or not.
- operator_callable = self._get_operator(self.operator) # type: CallableOperator
+ operator_callable: CallableOperator = self._get_operator(self.operator)
return operator_callable(normalized_item, self.version)
- def filter(self, iterable, prereleases=None):
- # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
+ def filter(
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
yielded = False
found_prereleases = []
@@ -240,7 +222,7 @@ class _IndividualSpecifier(BaseSpecifier):
if self.contains(parsed_version, **kw):
# If our version is a prerelease, and we were not set to allow
- # prereleases, then we'll store it for later incase nothing
+ # 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
@@ -285,44 +267,46 @@ class LegacySpecifier(_IndividualSpecifier):
">": "greater_than",
}
- def _coerce_version(self, version):
- # type: (Union[ParsedVersion, str]) -> LegacyVersion
+ def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
+ super().__init__(spec, prereleases)
+
+ warnings.warn(
+ "Creating a LegacyVersion has been deprecated and will be "
+ "removed in the next major release",
+ DeprecationWarning,
+ )
+
+ def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion:
if not isinstance(version, LegacyVersion):
version = LegacyVersion(str(version))
return version
- def _compare_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective == self._coerce_version(spec)
- def _compare_not_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective != self._coerce_version(spec)
- def _compare_less_than_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective <= self._coerce_version(spec)
- def _compare_greater_than_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_greater_than_equal(
+ self, prospective: LegacyVersion, spec: str
+ ) -> bool:
return prospective >= self._coerce_version(spec)
- def _compare_less_than(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective < self._coerce_version(spec)
- def _compare_greater_than(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective > self._coerce_version(spec)
def _require_version_compare(
- fn # type: (Callable[[Specifier, ParsedVersion, str], bool])
-):
- # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool]
+ fn: Callable[["Specifier", ParsedVersion, str], bool]
+) -> Callable[["Specifier", ParsedVersion, str], bool]:
@functools.wraps(fn)
- def wrapped(self, prospective, spec):
- # type: (Specifier, ParsedVersion, str) -> bool
+ def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool:
if not isinstance(prospective, Version):
return False
return fn(self, prospective, spec)
@@ -439,8 +423,7 @@ class Specifier(_IndividualSpecifier):
}
@_require_version_compare
- def _compare_compatible(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_compatible(self, prospective: ParsedVersion, 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
@@ -449,15 +432,9 @@ class Specifier(_IndividualSpecifier):
# the other specifiers.
# We want everything but the last item in the version, but we want to
- # ignore post and dev releases and we want to treat the pre-release as
- # it's own separate segment.
+ # ignore suffix segments.
prefix = ".".join(
- list(
- itertools.takewhile(
- lambda x: (not x.startswith("post") and not x.startswith("dev")),
- _version_split(spec),
- )
- )[:-1]
+ list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
)
# Add the prefix notation to the end of our string
@@ -468,8 +445,7 @@ class Specifier(_IndividualSpecifier):
)
@_require_version_compare
- def _compare_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool:
# We need special logic to handle prefix matching
if spec.endswith(".*"):
@@ -509,13 +485,11 @@ class Specifier(_IndividualSpecifier):
return prospective == spec_version
@_require_version_compare
- def _compare_not_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool:
return not self._compare_equal(prospective, spec)
@_require_version_compare
- def _compare_less_than_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
@@ -523,8 +497,9 @@ class Specifier(_IndividualSpecifier):
return Version(prospective.public) <= Version(spec)
@_require_version_compare
- def _compare_greater_than_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_greater_than_equal(
+ self, prospective: ParsedVersion, spec: str
+ ) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
@@ -532,8 +507,7 @@ class Specifier(_IndividualSpecifier):
return Version(prospective.public) >= Version(spec)
@_require_version_compare
- def _compare_less_than(self, prospective, spec_str):
- # type: (ParsedVersion, str) -> bool
+ def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool:
# Convert our spec to a Version instance, since we'll want to work with
# it as a version.
@@ -559,8 +533,7 @@ class Specifier(_IndividualSpecifier):
return True
@_require_version_compare
- def _compare_greater_than(self, prospective, spec_str):
- # type: (ParsedVersion, str) -> bool
+ def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool:
# Convert our spec to a Version instance, since we'll want to work with
# it as a version.
@@ -591,13 +564,11 @@ class Specifier(_IndividualSpecifier):
# same version in the spec.
return True
- def _compare_arbitrary(self, prospective, spec):
- # type: (Version, str) -> bool
+ def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
return str(prospective).lower() == str(spec).lower()
@property
- def prereleases(self):
- # type: () -> bool
+ def prereleases(self) -> bool:
# If there is an explicit prereleases set for this, then we'll just
# blindly use that.
@@ -622,17 +593,15 @@ class Specifier(_IndividualSpecifier):
return False
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
-def _version_split(version):
- # type: (str) -> List[str]
- result = [] # type: List[str]
+def _version_split(version: str) -> List[str]:
+ result: List[str] = []
for item in version.split("."):
match = _prefix_regex.search(item)
if match:
@@ -642,8 +611,13 @@ def _version_split(version):
return result
-def _pad_version(left, right):
- # type: (List[str], List[str]) -> Tuple[List[str], List[str]]
+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
@@ -662,8 +636,9 @@ def _pad_version(left, right):
class SpecifierSet(BaseSpecifier):
- def __init__(self, specifiers="", prereleases=None):
- # type: (str, Optional[bool]) -> None
+ def __init__(
+ self, specifiers: str = "", prereleases: Optional[bool] = None
+ ) -> None:
# Split on , to break each individual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace.
@@ -671,7 +646,7 @@ class SpecifierSet(BaseSpecifier):
# Parsed each individual specifier, attempting first to make it a
# Specifier and falling back to a LegacySpecifier.
- parsed = set()
+ parsed: Set[_IndividualSpecifier] = set()
for specifier in split_specifiers:
try:
parsed.add(Specifier(specifier))
@@ -685,27 +660,23 @@ class SpecifierSet(BaseSpecifier):
# we accept prereleases or not.
self._prereleases = prereleases
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
pre = (
- ", prereleases={0!r}".format(self.prereleases)
+ f", prereleases={self.prereleases!r}"
if self._prereleases is not None
else ""
)
- return "<SpecifierSet({0!r}{1})>".format(str(self), pre)
+ return "<SpecifierSet({!r}{})>".format(str(self), pre)
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return ",".join(sorted(str(s) for s in self._specs))
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._specs)
- def __and__(self, other):
- # type: (Union[SpecifierSet, str]) -> SpecifierSet
- if isinstance(other, string_types):
+ def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":
+ if isinstance(other, str):
other = SpecifierSet(other)
elif not isinstance(other, SpecifierSet):
return NotImplemented
@@ -727,35 +698,30 @@ class SpecifierSet(BaseSpecifier):
return specifier
- def __eq__(self, other):
- # type: (object) -> bool
- if isinstance(other, (string_types, _IndividualSpecifier)):
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, (str, _IndividualSpecifier)):
other = SpecifierSet(str(other))
elif not isinstance(other, SpecifierSet):
return NotImplemented
return self._specs == other._specs
- def __ne__(self, other):
- # type: (object) -> bool
- if isinstance(other, (string_types, _IndividualSpecifier)):
+ def __ne__(self, other: object) -> bool:
+ if isinstance(other, (str, _IndividualSpecifier)):
other = SpecifierSet(str(other))
elif not isinstance(other, SpecifierSet):
return NotImplemented
return self._specs != other._specs
- def __len__(self):
- # type: () -> int
+ def __len__(self) -> int:
return len(self._specs)
- def __iter__(self):
- # type: () -> Iterator[FrozenSet[_IndividualSpecifier]]
+ def __iter__(self) -> Iterator[_IndividualSpecifier]:
return iter(self._specs)
@property
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
# If we have been given an explicit prerelease modifier, then we'll
# pass that through here.
@@ -773,16 +739,15 @@ class SpecifierSet(BaseSpecifier):
return any(s.prereleases for s in self._specs)
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
- def __contains__(self, item):
- # type: (Union[ParsedVersion, str]) -> bool
+ def __contains__(self, item: UnparsedVersion) -> bool:
return self.contains(item)
- def contains(self, item, prereleases=None):
- # type: (Union[ParsedVersion, str], Optional[bool]) -> bool
+ def contains(
+ self, item: UnparsedVersion, prereleases: Optional[bool] = None
+ ) -> bool:
# Ensure that our item is a Version or LegacyVersion instance.
if not isinstance(item, (LegacyVersion, Version)):
@@ -810,11 +775,8 @@ class SpecifierSet(BaseSpecifier):
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
def filter(
- self,
- iterable, # type: Iterable[Union[ParsedVersion, str]]
- prereleases=None, # type: Optional[bool]
- ):
- # type: (...) -> Iterable[Union[ParsedVersion, str]]
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
# 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
@@ -833,8 +795,11 @@ class SpecifierSet(BaseSpecifier):
# which will filter out any pre-releases, unless there are no final
# releases, and which will filter out LegacyVersion in general.
else:
- filtered = [] # type: List[Union[ParsedVersion, str]]
- found_prereleases = [] # type: List[Union[ParsedVersion, str]]
+ filtered: List[VersionTypeVar] = []
+ found_prereleases: List[VersionTypeVar] = []
+
+ item: UnparsedVersion
+ parsed_version: Union[Version, LegacyVersion]
for item in iterable:
# Ensure that we some kind of Version class for this item.
diff --git a/pkg_resources/_vendor/packaging/tags.py b/pkg_resources/_vendor/packaging/tags.py
index 9064910b..e65890a9 100644
--- a/pkg_resources/_vendor/packaging/tags.py
+++ b/pkg_resources/_vendor/packaging/tags.py
@@ -2,62 +2,44 @@
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import
-
-import distutils.util
-
-try:
- from importlib.machinery import EXTENSION_SUFFIXES
-except ImportError: # pragma: no cover
- import imp
-
- EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()]
- del imp
import logging
-import os
import platform
-import re
-import struct
import sys
import sysconfig
-import warnings
-
-from ._typing import TYPE_CHECKING, cast
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import (
- Dict,
- FrozenSet,
- IO,
- Iterable,
- Iterator,
- List,
- Optional,
- Sequence,
- Tuple,
- Union,
- )
-
- PythonVersion = Sequence[int]
- MacVersion = Tuple[int, int]
- GlibcVersion = Tuple[int, int]
-
+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__)
-INTERPRETER_SHORT_NAMES = {
+PythonVersion = Sequence[int]
+MacVersion = Tuple[int, int]
+
+INTERPRETER_SHORT_NAMES: Dict[str, str] = {
"python": "py", # Generic.
"cpython": "cp",
"pypy": "pp",
"ironpython": "ip",
"jython": "jy",
-} # type: Dict[str, str]
+}
_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
-class Tag(object):
+class Tag:
"""
A representation of the tag triple for a wheel.
@@ -65,55 +47,53 @@ class Tag(object):
is also supported.
"""
- __slots__ = ["_interpreter", "_abi", "_platform"]
+ __slots__ = ["_interpreter", "_abi", "_platform", "_hash"]
- def __init__(self, interpreter, abi, platform):
- # type: (str, str, str) -> None
+ 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):
- # type: () -> str
+ def interpreter(self) -> str:
return self._interpreter
@property
- def abi(self):
- # type: () -> str
+ def abi(self) -> str:
return self._abi
@property
- def platform(self):
- # type: () -> str
+ def platform(self) -> str:
return self._platform
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
if not isinstance(other, Tag):
return NotImplemented
return (
- (self.platform == other.platform)
- and (self.abi == other.abi)
- and (self.interpreter == other.interpreter)
+ (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):
- # type: () -> int
- return hash((self._interpreter, self._abi, self._platform))
+ def __hash__(self) -> int:
+ return self._hash
- def __str__(self):
- # type: () -> str
- return "{}-{}-{}".format(self._interpreter, self._abi, self._platform)
+ def __str__(self) -> str:
+ return f"{self._interpreter}-{self._abi}-{self._platform}"
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
return "<{self} @ {self_id}>".format(self=self, self_id=id(self))
-def parse_tag(tag):
- # type: (str) -> FrozenSet[Tag]
+def parse_tag(tag: str) -> FrozenSet[Tag]:
"""
Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
@@ -129,24 +109,7 @@ def parse_tag(tag):
return frozenset(tags)
-def _warn_keyword_parameter(func_name, kwargs):
- # type: (str, Dict[str, bool]) -> bool
- """
- Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only.
- """
- if not kwargs:
- return False
- elif len(kwargs) > 1 or "warn" not in kwargs:
- kwargs.pop("warn", None)
- arg = next(iter(kwargs.keys()))
- raise TypeError(
- "{}() got an unexpected keyword argument {!r}".format(func_name, arg)
- )
- return kwargs["warn"]
-
-
-def _get_config_var(name, warn=False):
- # type: (str, bool) -> Union[int, str, None]
+def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]:
value = sysconfig.get_config_var(name)
if value is None and warn:
logger.debug(
@@ -155,13 +118,11 @@ def _get_config_var(name, warn=False):
return value
-def _normalize_string(string):
- # type: (str) -> str
+def _normalize_string(string: str) -> str:
return string.replace(".", "_").replace("-", "_")
-def _abi3_applies(python_version):
- # type: (PythonVersion) -> bool
+def _abi3_applies(python_version: PythonVersion) -> bool:
"""
Determine if the Python version supports abi3.
@@ -170,8 +131,7 @@ def _abi3_applies(python_version):
return len(python_version) > 1 and tuple(python_version) >= (3, 2)
-def _cpython_abis(py_version, warn=False):
- # type: (PythonVersion, bool) -> List[str]
+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])
@@ -197,7 +157,7 @@ def _cpython_abis(py_version, warn=False):
elif debug:
# Debug builds can also load "normal" extension modules.
# We can also assume no UCS-4 or pymalloc requirement.
- abis.append("cp{version}".format(version=version))
+ abis.append(f"cp{version}")
abis.insert(
0,
"cp{version}{debug}{pymalloc}{ucs4}".format(
@@ -208,12 +168,12 @@ def _cpython_abis(py_version, warn=False):
def cpython_tags(
- python_version=None, # type: Optional[PythonVersion]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
-):
- # type: (...) -> Iterator[Tag]
+ 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.
@@ -229,7 +189,6 @@ def cpython_tags(
If 'abi3' or 'none' are specified in 'abis' then they will be yielded at
their normal position and not at the beginning.
"""
- warn = _warn_keyword_parameter("cpython_tags", kwargs)
if not python_version:
python_version = sys.version_info[:2]
@@ -248,15 +207,13 @@ def cpython_tags(
except ValueError:
pass
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
for abi in abis:
for platform_ in platforms:
yield Tag(interpreter, abi, platform_)
if _abi3_applies(python_version):
- for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms):
- yield tag
- for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms):
- yield tag
+ yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
+ yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)
if _abi3_applies(python_version):
for minor_version in range(python_version[1] - 1, 1, -1):
@@ -267,20 +224,19 @@ def cpython_tags(
yield Tag(interpreter, "abi3", platform_)
-def _generic_abi():
- # type: () -> Iterator[str]
+def _generic_abi() -> Iterator[str]:
abi = sysconfig.get_config_var("SOABI")
if abi:
yield _normalize_string(abi)
def generic_tags(
- interpreter=None, # type: Optional[str]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
-):
- # type: (...) -> Iterator[Tag]
+ 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.
@@ -289,14 +245,13 @@ def generic_tags(
The "none" ABI will be added if it was not explicitly provided.
"""
- warn = _warn_keyword_parameter("generic_tags", kwargs)
if not interpreter:
interp_name = interpreter_name()
interp_version = interpreter_version(warn=warn)
interpreter = "".join([interp_name, interp_version])
if abis is None:
abis = _generic_abi()
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
abis = list(abis)
if "none" not in abis:
abis.append("none")
@@ -305,8 +260,7 @@ def generic_tags(
yield Tag(interpreter, abi, platform_)
-def _py_interpreter_range(py_version):
- # type: (PythonVersion) -> Iterator[str]
+def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
"""
Yields Python versions in descending order.
@@ -322,11 +276,10 @@ def _py_interpreter_range(py_version):
def compatible_tags(
- python_version=None, # type: Optional[PythonVersion]
- interpreter=None, # type: Optional[str]
- platforms=None, # type: Optional[Iterable[str]]
-):
- # type: (...) -> Iterator[Tag]
+ 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.
@@ -337,7 +290,7 @@ def compatible_tags(
"""
if not python_version:
python_version = sys.version_info[:2]
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
for version in _py_interpreter_range(python_version):
for platform_ in platforms:
yield Tag(version, "none", platform_)
@@ -347,8 +300,7 @@ def compatible_tags(
yield Tag(version, "none", "any")
-def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
- # type: (str, bool) -> str
+def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
if not is_32bit:
return arch
@@ -358,8 +310,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
return "i386"
-def _mac_binary_formats(version, cpu_arch):
- # type: (MacVersion, str) -> List[str]
+def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]:
formats = [cpu_arch]
if cpu_arch == "x86_64":
if version < (10, 4):
@@ -382,12 +333,18 @@ def _mac_binary_formats(version, cpu_arch):
return []
formats.extend(["fat32", "fat"])
- formats.append("universal")
+ 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=None, arch=None):
- # type: (Optional[MacVersion], Optional[str]) -> Iterator[str]
+def mac_platforms(
+ version: Optional[MacVersion] = None, arch: Optional[str] = None
+) -> Iterator[str]:
"""
Yields the platform tags for a macOS system.
@@ -396,7 +353,7 @@ def mac_platforms(version=None, arch=None):
generate platform tags for. Both parameters default to the appropriate value
for the current system.
"""
- version_str, _, cpu_arch = platform.mac_ver() # type: ignore
+ version_str, _, cpu_arch = platform.mac_ver()
if version is None:
version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
else:
@@ -405,283 +362,76 @@ def mac_platforms(version=None, arch=None):
arch = _mac_arch(cpu_arch)
else:
arch = arch
- for minor_version in range(version[1], -1, -1):
- compat_version = version[0], minor_version
- binary_formats = _mac_binary_formats(compat_version, arch)
- for binary_format in binary_formats:
- yield "macosx_{major}_{minor}_{binary_format}".format(
- major=compat_version[0],
- minor=compat_version[1],
- binary_format=binary_format,
- )
-
-
-# From PEP 513.
-def _is_manylinux_compatible(name, glibc_version):
- # type: (str, GlibcVersion) -> bool
- # Check for presence of _manylinux module.
- try:
- import _manylinux # noqa
-
- return bool(getattr(_manylinux, name + "_compatible"))
- except (ImportError, AttributeError):
- # Fall through to heuristic check below.
- pass
- return _have_compatible_glibc(*glibc_version)
-
-
-def _glibc_version_string():
- # type: () -> Optional[str]
- # Returns glibc version string, or None if not using glibc.
- return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
-
-
-def _glibc_version_string_confstr():
- # type: () -> Optional[str]
- """
- Primary implementation of glibc_version_string using os.confstr.
- """
- # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
- # to be broken or missing. This strategy is used in the standard library
- # platform module.
- # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183
- try:
- # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
- version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821
- "CS_GNU_LIBC_VERSION"
- )
- assert version_string is not None
- _, version = version_string.split() # type: Tuple[str, str]
- except (AssertionError, AttributeError, OSError, ValueError):
- # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
- return None
- return version
+ 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
+ )
-def _glibc_version_string_ctypes():
- # type: () -> Optional[str]
- """
- Fallback implementation of glibc_version_string using ctypes.
- """
- try:
- import ctypes
- except ImportError:
- return None
-
- # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
- # manpage says, "If filename is NULL, then the returned handle is for the
- # main program". This way we can let the linker do the work to figure out
- # which libc our process is actually using.
- #
- # Note: typeshed is wrong here so we are ignoring this line.
- process_namespace = ctypes.CDLL(None) # type: ignore
- try:
- gnu_get_libc_version = process_namespace.gnu_get_libc_version
- except AttributeError:
- # Symbol doesn't exist -> therefore, we are not linked to
- # glibc.
- return None
-
- # Call gnu_get_libc_version, which returns a string like "2.5"
- gnu_get_libc_version.restype = ctypes.c_char_p
- version_str = gnu_get_libc_version() # type: str
- # py2 / py3 compatibility:
- if not isinstance(version_str, str):
- version_str = version_str.decode("ascii")
-
- return version_str
-
-
-# Separated out from have_compatible_glibc for easier unit testing.
-def _check_glibc_version(version_str, required_major, minimum_minor):
- # type: (str, int, int) -> bool
- # Parse string and check against requested version.
- #
- # We use a regexp instead of str.split because we want to discard any
- # random junk that might come after the minor version -- this might happen
- # in patched/forked versions of glibc (e.g. Linaro's version of glibc
- # uses version strings like "2.20-2014.11"). See gh-3588.
- m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
- if not m:
- warnings.warn(
- "Expected glibc version with 2 components major.minor,"
- " got: %s" % version_str,
- RuntimeWarning,
- )
- return False
- return (
- int(m.group("major")) == required_major
- and int(m.group("minor")) >= minimum_minor
- )
+ 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 _have_compatible_glibc(required_major, minimum_minor):
- # type: (int, int) -> bool
- version_str = _glibc_version_string()
- if version_str is None:
- return False
- return _check_glibc_version(version_str, required_major, minimum_minor)
-
-
-# Python does not provide platform information at sufficient granularity to
-# identify the architecture of the running executable in some cases, so we
-# determine it dynamically by reading the information from the running
-# process. This only applies on Linux, which uses the ELF format.
-class _ELFFileHeader(object):
- # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
- class _InvalidELFFileHeader(ValueError):
- """
- An invalid ELF file header was found.
- """
-
- ELF_MAGIC_NUMBER = 0x7F454C46
- ELFCLASS32 = 1
- ELFCLASS64 = 2
- ELFDATA2LSB = 1
- ELFDATA2MSB = 2
- EM_386 = 3
- EM_S390 = 22
- EM_ARM = 40
- EM_X86_64 = 62
- EF_ARM_ABIMASK = 0xFF000000
- EF_ARM_ABI_VER5 = 0x05000000
- EF_ARM_ABI_FLOAT_HARD = 0x00000400
-
- def __init__(self, file):
- # type: (IO[bytes]) -> None
- def unpack(fmt):
- # type: (str) -> int
- try:
- (result,) = struct.unpack(
- fmt, file.read(struct.calcsize(fmt))
- ) # type: (int, )
- except struct.error:
- raise _ELFFileHeader._InvalidELFFileHeader()
- return result
-
- self.e_ident_magic = unpack(">I")
- if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_class = unpack("B")
- if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_data = unpack("B")
- if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_version = unpack("B")
- self.e_ident_osabi = unpack("B")
- self.e_ident_abiversion = unpack("B")
- self.e_ident_pad = file.read(7)
- format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
- format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
- format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
- format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
- self.e_type = unpack(format_h)
- self.e_machine = unpack(format_h)
- self.e_version = unpack(format_i)
- self.e_entry = unpack(format_p)
- self.e_phoff = unpack(format_p)
- self.e_shoff = unpack(format_p)
- self.e_flags = unpack(format_i)
- self.e_ehsize = unpack(format_h)
- self.e_phentsize = unpack(format_h)
- self.e_phnum = unpack(format_h)
- self.e_shentsize = unpack(format_h)
- self.e_shnum = unpack(format_h)
- self.e_shstrndx = unpack(format_h)
-
-
-def _get_elf_header():
- # type: () -> Optional[_ELFFileHeader]
- try:
- with open(sys.executable, "rb") as f:
- elf_header = _ELFFileHeader(f)
- except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
- return None
- return elf_header
-
-
-def _is_linux_armhf():
- # type: () -> bool
- # hard-float ABI can be detected from the ELF header of the running
- # process
- # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_ARM
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABIMASK
- ) == elf_header.EF_ARM_ABI_VER5
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD
- ) == elf_header.EF_ARM_ABI_FLOAT_HARD
- return result
-
-
-def _is_linux_i686():
- # type: () -> bool
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_386
- return result
-
-
-def _have_compatible_manylinux_abi(arch):
- # type: (str) -> bool
- if arch == "armv7l":
- return _is_linux_armhf()
- if arch == "i686":
- return _is_linux_i686()
- return True
-
-
-def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
- # type: (bool) -> Iterator[str]
- linux = _normalize_string(distutils.util.get_platform())
+def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
+ linux = _normalize_string(sysconfig.get_platform())
if is_32bit:
if linux == "linux_x86_64":
linux = "linux_i686"
elif linux == "linux_aarch64":
linux = "linux_armv7l"
- manylinux_support = []
_, arch = linux.split("_", 1)
- if _have_compatible_manylinux_abi(arch):
- if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}:
- manylinux_support.append(
- ("manylinux2014", (2, 17))
- ) # CentOS 7 w/ glibc 2.17 (PEP 599)
- if arch in {"x86_64", "i686"}:
- manylinux_support.append(
- ("manylinux2010", (2, 12))
- ) # CentOS 6 w/ glibc 2.12 (PEP 571)
- manylinux_support.append(
- ("manylinux1", (2, 5))
- ) # CentOS 5 w/ glibc 2.5 (PEP 513)
- manylinux_support_iter = iter(manylinux_support)
- for name, glibc_version in manylinux_support_iter:
- if _is_manylinux_compatible(name, glibc_version):
- yield linux.replace("linux", name)
- break
- # Support for a later manylinux implies support for an earlier version.
- for name, _ in manylinux_support_iter:
- yield linux.replace("linux", name)
+ yield from _manylinux.platform_tags(linux, arch)
+ yield from _musllinux.platform_tags(arch)
yield linux
-def _generic_platforms():
- # type: () -> Iterator[str]
- yield _normalize_string(distutils.util.get_platform())
+def _generic_platforms() -> Iterator[str]:
+ yield _normalize_string(sysconfig.get_platform())
-def _platform_tags():
- # type: () -> Iterator[str]
+def platform_tags() -> Iterator[str]:
"""
Provides the platform tags for this installation.
"""
@@ -693,25 +443,18 @@ def _platform_tags():
return _generic_platforms()
-def interpreter_name():
- # type: () -> str
+def interpreter_name() -> str:
"""
Returns the name of the running interpreter.
"""
- try:
- name = sys.implementation.name # type: ignore
- except AttributeError: # pragma: no cover
- # Python 2.7 compatibility.
- name = platform.python_implementation().lower()
+ name = sys.implementation.name
return INTERPRETER_SHORT_NAMES.get(name) or name
-def interpreter_version(**kwargs):
- # type: (bool) -> str
+def interpreter_version(*, warn: bool = False) -> str:
"""
Returns the version of the running interpreter.
"""
- warn = _warn_keyword_parameter("interpreter_version", kwargs)
version = _get_config_var("py_version_nodot", warn=warn)
if version:
version = str(version)
@@ -720,32 +463,22 @@ def interpreter_version(**kwargs):
return version
-def _version_nodot(version):
- # type: (PythonVersion) -> str
- if any(v >= 10 for v in version):
- sep = "_"
- else:
- sep = ""
- return sep.join(map(str, version))
+def _version_nodot(version: PythonVersion) -> str:
+ return "".join(map(str, version))
-def sys_tags(**kwargs):
- # type: (bool) -> Iterator[Tag]
+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.
"""
- warn = _warn_keyword_parameter("sys_tags", kwargs)
interp_name = interpreter_name()
if interp_name == "cp":
- for tag in cpython_tags(warn=warn):
- yield tag
+ yield from cpython_tags(warn=warn)
else:
- for tag in generic_tags():
- yield tag
+ yield from generic_tags()
- for tag in compatible_tags():
- yield tag
+ yield from compatible_tags()
diff --git a/pkg_resources/_vendor/packaging/utils.py b/pkg_resources/_vendor/packaging/utils.py
index 19579c1a..bab11b80 100644
--- a/pkg_resources/_vendor/packaging/utils.py
+++ b/pkg_resources/_vendor/packaging/utils.py
@@ -1,65 +1,136 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import re
+from typing import FrozenSet, NewType, Tuple, Union, cast
-from ._typing import TYPE_CHECKING, cast
+from .tags import Tag, parse_tag
from .version import InvalidVersion, Version
-if TYPE_CHECKING: # pragma: no cover
- from typing import NewType, Union
+BuildTag = Union[Tuple[()], Tuple[int, str]]
+NormalizedName = NewType("NormalizedName", str)
+
+
+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.
+ """
- NormalizedName = NewType("NormalizedName", str)
_canonicalize_regex = re.compile(r"[-_.]+")
+# PEP 427: The build number must start with a digit.
+_build_tag_regex = re.compile(r"(\d+)(.*)")
-def canonicalize_name(name):
- # type: (str) -> NormalizedName
+def canonicalize_name(name: str) -> NormalizedName:
# This is taken from PEP 503.
value = _canonicalize_regex.sub("-", name).lower()
- return cast("NormalizedName", value)
+ return cast(NormalizedName, value)
-def canonicalize_version(_version):
- # type: (str) -> Union[Version, str]
+def canonicalize_version(version: Union[Version, str]) -> str:
"""
This is very similar to Version.__str__, but has one subtle difference
with the way it handles the release segment.
"""
-
- try:
- version = Version(_version)
- except InvalidVersion:
- # Legacy versions cannot be normalized
- return _version
+ if isinstance(version, str):
+ try:
+ parsed = Version(version)
+ except InvalidVersion:
+ # Legacy versions cannot be normalized
+ return version
+ else:
+ parsed = version
parts = []
# Epoch
- if version.epoch != 0:
- parts.append("{0}!".format(version.epoch))
+ if parsed.epoch != 0:
+ parts.append(f"{parsed.epoch}!")
# Release segment
# NB: This strips trailing '.0's to normalize
- parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release)))
+ parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release)))
# Pre-release
- if version.pre is not None:
- parts.append("".join(str(x) for x in version.pre))
+ if parsed.pre is not None:
+ parts.append("".join(str(x) for x in parsed.pre))
# Post-release
- if version.post is not None:
- parts.append(".post{0}".format(version.post))
+ if parsed.post is not None:
+ parts.append(f".post{parsed.post}")
# Development release
- if version.dev is not None:
- parts.append(".dev{0}".format(version.dev))
+ if parsed.dev is not None:
+ parts.append(f".dev{parsed.dev}")
# Local version segment
- if version.local is not None:
- parts.append("+{0}".format(version.local))
+ 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)
+ version = Version(parts[1])
+ 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)
+ version = Version(version_part)
+ return (name, version)
diff --git a/pkg_resources/_vendor/packaging/version.py b/pkg_resources/_vendor/packaging/version.py
index 00371e86..de9a09a4 100644
--- a/pkg_resources/_vendor/packaging/version.py
+++ b/pkg_resources/_vendor/packaging/version.py
@@ -1,52 +1,45 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import collections
import itertools
import re
+import warnings
+from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
-from ._structures import Infinity, NegativeInfinity
-from ._typing import TYPE_CHECKING
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
-
- from ._structures import InfinityType, NegativeInfinityType
-
- InfiniteTypes = Union[InfinityType, NegativeInfinityType]
- PrePostDevType = Union[InfiniteTypes, Tuple[str, int]]
- SubLocalType = Union[InfiniteTypes, int, str]
- LocalType = Union[
- NegativeInfinityType,
- Tuple[
- Union[
- SubLocalType,
- Tuple[SubLocalType, str],
- Tuple[NegativeInfinityType, SubLocalType],
- ],
- ...,
- ],
- ]
- CmpKey = Tuple[
- int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
- ]
- LegacyCmpKey = Tuple[int, Tuple[str, ...]]
- VersionComparisonMethod = Callable[
- [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
- ]
+from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
+InfiniteTypes = Union[InfinityType, NegativeInfinityType]
+PrePostDevType = Union[InfiniteTypes, Tuple[str, int]]
+SubLocalType = Union[InfiniteTypes, int, str]
+LocalType = Union[
+ NegativeInfinityType,
+ Tuple[
+ Union[
+ SubLocalType,
+ Tuple[SubLocalType, str],
+ Tuple[NegativeInfinityType, SubLocalType],
+ ],
+ ...,
+ ],
+]
+CmpKey = Tuple[
+ int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
+]
+LegacyCmpKey = Tuple[int, Tuple[str, ...]]
+VersionComparisonMethod = Callable[
+ [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
+]
_Version = collections.namedtuple(
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
)
-def parse(version):
- # type: (str) -> Union[LegacyVersion, Version]
+def parse(version: str) -> Union["LegacyVersion", "Version"]:
"""
Parse the given version string and return either a :class:`Version` object
or a :class:`LegacyVersion` object depending on if the given version is
@@ -64,112 +57,111 @@ class InvalidVersion(ValueError):
"""
-class _BaseVersion(object):
- _key = None # type: Union[CmpKey, LegacyCmpKey]
+class _BaseVersion:
+ _key: Union[CmpKey, LegacyCmpKey]
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._key)
- def __lt__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s < o)
+ # 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 __le__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s <= o)
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
- def __eq__(self, other):
- # type: (object) -> bool
- return self._compare(other, lambda s, o: s == o)
+ return self._key == other._key
- def __ge__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s >= o)
+ def __ge__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
- def __gt__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s > o)
+ return self._key >= other._key
- def __ne__(self, other):
- # type: (object) -> bool
- return self._compare(other, lambda s, o: s != o)
+ def __gt__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
+
+ return self._key > other._key
- def _compare(self, other, method):
- # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented]
+ def __ne__(self, other: object) -> bool:
if not isinstance(other, _BaseVersion):
return NotImplemented
- return method(self._key, other._key)
+ return self._key != other._key
class LegacyVersion(_BaseVersion):
- def __init__(self, version):
- # type: (str) -> None
+ def __init__(self, version: str) -> None:
self._version = str(version)
self._key = _legacy_cmpkey(self._version)
- def __str__(self):
- # type: () -> str
+ warnings.warn(
+ "Creating a LegacyVersion has been deprecated and will be "
+ "removed in the next major release",
+ DeprecationWarning,
+ )
+
+ def __str__(self) -> str:
return self._version
- def __repr__(self):
- # type: () -> str
- return "<LegacyVersion({0})>".format(repr(str(self)))
+ def __repr__(self) -> str:
+ return f"<LegacyVersion('{self}')>"
@property
- def public(self):
- # type: () -> str
+ def public(self) -> str:
return self._version
@property
- def base_version(self):
- # type: () -> str
+ def base_version(self) -> str:
return self._version
@property
- def epoch(self):
- # type: () -> int
+ def epoch(self) -> int:
return -1
@property
- def release(self):
- # type: () -> None
+ def release(self) -> None:
return None
@property
- def pre(self):
- # type: () -> None
+ def pre(self) -> None:
return None
@property
- def post(self):
- # type: () -> None
+ def post(self) -> None:
return None
@property
- def dev(self):
- # type: () -> None
+ def dev(self) -> None:
return None
@property
- def local(self):
- # type: () -> None
+ def local(self) -> None:
return None
@property
- def is_prerelease(self):
- # type: () -> bool
+ def is_prerelease(self) -> bool:
return False
@property
- def is_postrelease(self):
- # type: () -> bool
+ def is_postrelease(self) -> bool:
return False
@property
- def is_devrelease(self):
- # type: () -> bool
+ def is_devrelease(self) -> bool:
return False
@@ -184,8 +176,7 @@ _legacy_version_replacement_map = {
}
-def _parse_version_parts(s):
- # type: (str) -> Iterator[str]
+def _parse_version_parts(s: str) -> Iterator[str]:
for part in _legacy_version_component_re.split(s):
part = _legacy_version_replacement_map.get(part, part)
@@ -202,8 +193,7 @@ def _parse_version_parts(s):
yield "*final"
-def _legacy_cmpkey(version):
- # type: (str) -> LegacyCmpKey
+def _legacy_cmpkey(version: str) -> LegacyCmpKey:
# We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
# greater than or equal to 0. This will effectively put the LegacyVersion,
@@ -213,7 +203,7 @@ def _legacy_cmpkey(version):
# This scheme is taken from pkg_resources.parse_version setuptools prior to
# it's adoption of the packaging library.
- parts = [] # type: List[str]
+ parts: List[str] = []
for part in _parse_version_parts(version.lower()):
if part.startswith("*"):
# remove "-" before a prerelease tag
@@ -268,13 +258,12 @@ class Version(_BaseVersion):
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
- def __init__(self, version):
- # type: (str) -> None
+ def __init__(self, version: str) -> None:
# Validate the version and parse it into pieces
match = self._regex.search(version)
if not match:
- raise InvalidVersion("Invalid version: '{0}'".format(version))
+ raise InvalidVersion(f"Invalid version: '{version}'")
# Store the parsed out pieces of the version
self._version = _Version(
@@ -298,17 +287,15 @@ class Version(_BaseVersion):
self._version.local,
)
- def __repr__(self):
- # type: () -> str
- return "<Version({0})>".format(repr(str(self)))
+ def __repr__(self) -> str:
+ return f"<Version('{self}')>"
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
parts = []
# Epoch
if self.epoch != 0:
- parts.append("{0}!".format(self.epoch))
+ parts.append(f"{self.epoch}!")
# Release segment
parts.append(".".join(str(x) for x in self.release))
@@ -319,67 +306,59 @@ class Version(_BaseVersion):
# Post-release
if self.post is not None:
- parts.append(".post{0}".format(self.post))
+ parts.append(f".post{self.post}")
# Development release
if self.dev is not None:
- parts.append(".dev{0}".format(self.dev))
+ parts.append(f".dev{self.dev}")
# Local version segment
if self.local is not None:
- parts.append("+{0}".format(self.local))
+ parts.append(f"+{self.local}")
return "".join(parts)
@property
- def epoch(self):
- # type: () -> int
- _epoch = self._version.epoch # type: int
+ def epoch(self) -> int:
+ _epoch: int = self._version.epoch
return _epoch
@property
- def release(self):
- # type: () -> Tuple[int, ...]
- _release = self._version.release # type: Tuple[int, ...]
+ def release(self) -> Tuple[int, ...]:
+ _release: Tuple[int, ...] = self._version.release
return _release
@property
- def pre(self):
- # type: () -> Optional[Tuple[str, int]]
- _pre = self._version.pre # type: Optional[Tuple[str, int]]
+ def pre(self) -> Optional[Tuple[str, int]]:
+ _pre: Optional[Tuple[str, int]] = self._version.pre
return _pre
@property
- def post(self):
- # type: () -> Optional[Tuple[str, int]]
+ def post(self) -> Optional[int]:
return self._version.post[1] if self._version.post else None
@property
- def dev(self):
- # type: () -> Optional[Tuple[str, int]]
+ def dev(self) -> Optional[int]:
return self._version.dev[1] if self._version.dev else None
@property
- def local(self):
- # type: () -> Optional[str]
+ def local(self) -> Optional[str]:
if self._version.local:
return ".".join(str(x) for x in self._version.local)
else:
return None
@property
- def public(self):
- # type: () -> str
+ def public(self) -> str:
return str(self).split("+", 1)[0]
@property
- def base_version(self):
- # type: () -> str
+ def base_version(self) -> str:
parts = []
# Epoch
if self.epoch != 0:
- parts.append("{0}!".format(self.epoch))
+ parts.append(f"{self.epoch}!")
# Release segment
parts.append(".".join(str(x) for x in self.release))
@@ -387,41 +366,33 @@ class Version(_BaseVersion):
return "".join(parts)
@property
- def is_prerelease(self):
- # type: () -> bool
+ def is_prerelease(self) -> bool:
return self.dev is not None or self.pre is not None
@property
- def is_postrelease(self):
- # type: () -> bool
+ def is_postrelease(self) -> bool:
return self.post is not None
@property
- def is_devrelease(self):
- # type: () -> bool
+ def is_devrelease(self) -> bool:
return self.dev is not None
@property
- def major(self):
- # type: () -> int
+ def major(self) -> int:
return self.release[0] if len(self.release) >= 1 else 0
@property
- def minor(self):
- # type: () -> int
+ def minor(self) -> int:
return self.release[1] if len(self.release) >= 2 else 0
@property
- def micro(self):
- # type: () -> int
+ def micro(self) -> int:
return self.release[2] if len(self.release) >= 3 else 0
def _parse_letter_version(
- letter, # type: str
- number, # type: Union[str, bytes, SupportsInt]
-):
- # type: (...) -> Optional[Tuple[str, int]]
+ letter: str, number: Union[str, bytes, SupportsInt]
+) -> Optional[Tuple[str, int]]:
if letter:
# We consider there to be an implicit 0 in a pre-release if there is
@@ -458,8 +429,7 @@ def _parse_letter_version(
_local_version_separators = re.compile(r"[\._-]")
-def _parse_local_version(local):
- # type: (str) -> Optional[LocalType]
+def _parse_local_version(local: str) -> Optional[LocalType]:
"""
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
"""
@@ -472,14 +442,13 @@ def _parse_local_version(local):
def _cmpkey(
- epoch, # type: int
- release, # type: Tuple[int, ...]
- pre, # type: Optional[Tuple[str, int]]
- post, # type: Optional[Tuple[str, int]]
- dev, # type: Optional[Tuple[str, int]]
- local, # type: Optional[Tuple[SubLocalType]]
-):
- # type: (...) -> CmpKey
+ epoch: int,
+ release: Tuple[int, ...],
+ pre: Optional[Tuple[str, int]],
+ post: Optional[Tuple[str, int]],
+ dev: Optional[Tuple[str, int]],
+ local: Optional[Tuple[SubLocalType]],
+) -> 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
@@ -495,7 +464,7 @@ def _cmpkey(
# if there is not a pre or a post segment. If we have one of those then
# the normal sorting rules will handle this case correctly.
if pre is None and post is None and dev is not None:
- _pre = NegativeInfinity # type: PrePostDevType
+ _pre: PrePostDevType = NegativeInfinity
# Versions without a pre-release (except as noted above) should sort after
# those with one.
elif pre is None:
@@ -505,21 +474,21 @@ def _cmpkey(
# Versions without a post segment should sort before those with one.
if post is None:
- _post = NegativeInfinity # type: PrePostDevType
+ _post: PrePostDevType = NegativeInfinity
else:
_post = post
# Versions without a development segment should sort after those with one.
if dev is None:
- _dev = Infinity # type: PrePostDevType
+ _dev: PrePostDevType = Infinity
else:
_dev = dev
if local is None:
# Versions without a local segment should sort before those with one.
- _local = NegativeInfinity # type: LocalType
+ _local: LocalType = NegativeInfinity
else:
# Versions with a local segment need that segment parsed to implement
# the sorting rules in PEP440.
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
new file mode 100644
index 00000000..e1187231
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
@@ -0,0 +1,3 @@
+UNKNOWN
+
+
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/INSTALLER b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt
new file mode 100644
index 00000000..bbc959e0
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt
@@ -0,0 +1,18 @@
+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/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/METADATA b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/METADATA
new file mode 100644
index 00000000..a15c350e
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/METADATA
@@ -0,0 +1,30 @@
+Metadata-Version: 2.0
+Name: pyparsing
+Version: 2.2.1
+Summary: Python parsing module
+Home-page: https://github.com/pyparsing/pyparsing/
+Author: Paul McGuire
+Author-email: ptmcg@users.sourceforge.net
+License: MIT License
+Download-URL: https://pypi.org/project/pyparsing/
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
+
+UNKNOWN
+
+
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/RECORD b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/RECORD
new file mode 100644
index 00000000..09cc30e3
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/RECORD
@@ -0,0 +1,11 @@
+__pycache__/pyparsing.cpython-310.pyc,,
+pyparsing-2.2.1.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10
+pyparsing-2.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pyparsing-2.2.1.dist-info/LICENSE.txt,sha256=081Pq74Spe1XdwrGkewNKSqa078kLIh7UWI-wVjdj8I,1041
+pyparsing-2.2.1.dist-info/METADATA,sha256=I0jhx9vpUYlQXjn4gVDnFFoAt3nNrxwR4iuqA_pknYs,1091
+pyparsing-2.2.1.dist-info/RECORD,,
+pyparsing-2.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pyparsing-2.2.1.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
+pyparsing-2.2.1.dist-info/metadata.json,sha256=v1_77-dSdajUZSItSJg8Ov9M713STY3PzhyrRvs1ax4,1185
+pyparsing-2.2.1.dist-info/top_level.txt,sha256=eUOjGzJVhlQ3WS2rFAy2mN3LX_7FKTM5GSJ04jfnLmU,10
+pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/REQUESTED b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/REQUESTED
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/WHEEL b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/WHEEL
new file mode 100644
index 00000000..7332a419
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/metadata.json b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/metadata.json
new file mode 100644
index 00000000..b760b766
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7"], "download_url": "https://pypi.org/project/pyparsing/", "extensions": {"python.details": {"contacts": [{"email": "ptmcg@users.sourceforge.net", "name": "Paul McGuire", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://github.com/pyparsing/pyparsing/"}}}, "generator": "bdist_wheel (0.30.0)", "license": "MIT License", "metadata_version": "2.0", "name": "pyparsing", "requires_python": ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", "summary": "Python parsing module", "version": "2.2.1"} \ No newline at end of file
diff --git a/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/top_level.txt b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/top_level.txt
new file mode 100644
index 00000000..210dfec5
--- /dev/null
+++ b/pkg_resources/_vendor/pyparsing-2.2.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+pyparsing
diff --git a/pkg_resources/_vendor/pyparsing.py b/pkg_resources/_vendor/pyparsing.py
index 4cae7883..cf75e1e5 100644
--- a/pkg_resources/_vendor/pyparsing.py
+++ b/pkg_resources/_vendor/pyparsing.py
@@ -1625,7 +1625,7 @@ class ParserElement(object):
(see L{I{parseWithTabs}<parseWithTabs>})
- define your parse action using the full C{(s,loc,toks)} signature, and
reference the input string using the parse action's C{s} argument
- - explicitly expand the tabs in your input string before calling
+ - explictly expand the tabs in your input string before calling
C{parseString}
Example::
diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt
index da7a1981..0128eb17 100644
--- a/pkg_resources/_vendor/vendored.txt
+++ b/pkg_resources/_vendor/vendored.txt
@@ -1,3 +1,8 @@
-packaging==20.4
+packaging==21.2
pyparsing==2.2.1
appdirs==1.4.3
+jaraco.text==3.7.0
+# required for jaraco.text on older Pythons
+importlib_resources==5.4.0
+# required for importlib_resources on older Pythons
+zipp==3.7.0
diff --git a/pkg_resources/_vendor/zipp-3.7.0.dist-info/INSTALLER b/pkg_resources/_vendor/zipp-3.7.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/pkg_resources/_vendor/zipp-3.7.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/pkg_resources/_vendor/zipp-3.7.0.dist-info/LICENSE b/pkg_resources/_vendor/zipp-3.7.0.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/pkg_resources/_vendor/zipp-3.7.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/pkg_resources/_vendor/zipp-3.7.0.dist-info/METADATA b/pkg_resources/_vendor/zipp-3.7.0.dist-info/METADATA
new file mode 100644
index 00000000..b1308b5f
--- /dev/null
+++ b/pkg_resources/_vendor/zipp-3.7.0.dist-info/METADATA
@@ -0,0 +1,58 @@
+Metadata-Version: 2.1
+Name: zipp
+Version: 3.7.0
+Summary: Backport of pathlib-compatible object wrapper for zip files
+Home-page: https://github.com/jaraco/zipp
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.7
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: jaraco.itertools ; extra == 'testing'
+Requires-Dist: func-timeout ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/zipp.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/zipp.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/zipp
+
+.. image:: https://github.com/jaraco/zipp/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/zipp/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest
+.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+
+A pathlib-compatible Zipfile object wrapper. Official backport of the standard library
+`Path object <https://docs.python.org/3.8/library/zipfile.html#path-objects>`_.
+
+
diff --git a/pkg_resources/_vendor/zipp-3.7.0.dist-info/RECORD b/pkg_resources/_vendor/zipp-3.7.0.dist-info/RECORD
new file mode 100644
index 00000000..38d0b21a
--- /dev/null
+++ b/pkg_resources/_vendor/zipp-3.7.0.dist-info/RECORD
@@ -0,0 +1,9 @@
+__pycache__/zipp.cpython-310.pyc,,
+zipp-3.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+zipp-3.7.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+zipp-3.7.0.dist-info/METADATA,sha256=ZLzgaXTyZX_MxTU0lcGfhdPY4CjFrT_3vyQ2Fo49pl8,2261
+zipp-3.7.0.dist-info/RECORD,,
+zipp-3.7.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+zipp-3.7.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+zipp-3.7.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5
+zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
diff --git a/pkg_resources/_vendor/zipp-3.7.0.dist-info/REQUESTED b/pkg_resources/_vendor/zipp-3.7.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/pkg_resources/_vendor/zipp-3.7.0.dist-info/REQUESTED
diff --git a/pkg_resources/_vendor/zipp-3.7.0.dist-info/WHEEL b/pkg_resources/_vendor/zipp-3.7.0.dist-info/WHEEL
new file mode 100644
index 00000000..becc9a66
--- /dev/null
+++ b/pkg_resources/_vendor/zipp-3.7.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/pkg_resources/_vendor/zipp-3.7.0.dist-info/top_level.txt b/pkg_resources/_vendor/zipp-3.7.0.dist-info/top_level.txt
new file mode 100644
index 00000000..e82f676f
--- /dev/null
+++ b/pkg_resources/_vendor/zipp-3.7.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+zipp
diff --git a/pkg_resources/_vendor/zipp.py b/pkg_resources/_vendor/zipp.py
new file mode 100644
index 00000000..26b723c1
--- /dev/null
+++ b/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/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py
index fed59295..70897eea 100644
--- a/pkg_resources/extern/__init__.py
+++ b/pkg_resources/extern/__init__.py
@@ -69,5 +69,8 @@ class VendorImporter:
sys.meta_path.append(self)
-names = 'packaging', 'pyparsing', 'appdirs'
+names = (
+ 'packaging', 'pyparsing', 'appdirs', 'jaraco', 'importlib_resources',
+ 'more_itertools',
+)
VendorImporter(__name__, names).install()
diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py
index 965a7c00..107dda7b 100644
--- a/pkg_resources/tests/test_resources.py
+++ b/pkg_resources/tests/test_resources.py
@@ -703,7 +703,7 @@ class TestParsing:
)
def test_local_version(self):
- req, = parse_requirements('foo==1.0.org1')
+ req, = parse_requirements('foo==1.0+org1')
def test_spaces_between_multiple_versions(self):
req, = parse_requirements('foo>=1.0, <3')
diff --git a/pyproject.toml b/pyproject.toml
index 03c40125..6b426a37 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -30,7 +30,7 @@ addopts = "-n auto"
directory = "changelog.d"
title_format = "v{version}"
issue_format = "#{issue}"
- template = "towncrier_template.rst"
+ template = "tools/towncrier_template.rst"
underlines = ["-", "^"]
[[tool.towncrier.type]]
diff --git a/pytest.ini b/pytest.ini
index df1c2af9..4a5ad50d 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -12,8 +12,19 @@ filterwarnings=
## upstream
# Suppress deprecation warning in flake8
ignore:SelectableGroups dict interface is deprecated::flake8
- # Suppress deprecation warning in pypa/packaging#433
- ignore:The distutils package is deprecated::packaging.tags
+
+ # shopkeep/pytest-black#55
+ ignore:<class 'pytest_black.BlackItem'> is not using a cooperative constructor:pytest.PytestDeprecationWarning
+ ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestDeprecationWarning
+ ignore:BlackItem is an Item subclass and should not be a collector:pytest.PytestWarning
+
+ # tholo/pytest-flake8#83
+ ignore:<class 'pytest_flake8.Flake8Item'> is not using a cooperative constructor:pytest.PytestDeprecationWarning
+ ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestDeprecationWarning
+ ignore:Flake8Item is an Item subclass and should not be a collector:pytest.PytestWarning
+
+ # dbader/pytest-mypy#131
+ ignore:The \(fspath. py.path.local\) argument to MypyFile is deprecated.:pytest.PytestDeprecationWarning
## end upstream
# https://github.com/pypa/setuptools/issues/1823
@@ -26,7 +37,7 @@ filterwarnings=
ignore:The Windows bytes API has been deprecated:DeprecationWarning
# https://github.com/pypa/setuptools/issues/2823
- ignore:setup_requires is deprecated.
+ ignore:setuptools.installer is deprecated.
# https://github.com/pypa/setuptools/issues/917
ignore:setup.py install is deprecated.
@@ -40,6 +51,7 @@ filterwarnings=
# SETUPTOOLS_USE_DISTUTILS=stdlib but for
# https://github.com/pytest-dev/pytest/discussions/9296
ignore:The distutils.sysconfig module is deprecated, use sysconfig instead
+ ignore:The distutils package is deprecated.*
# Workaround for pypa/setuptools#2868
# ideally would apply to PyPy only but for
diff --git a/setup.cfg b/setup.cfg
index 088f54d6..32d97498 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
[metadata]
name = setuptools
-version = 59.0.1
+version = 60.8.2
author = Python Packaging Authority
author_email = distutils-sig@python.org
description = Easily download, build, install, upgrade, and uninstall Python packages
@@ -18,13 +18,13 @@ classifiers =
Topic :: Utilities
keywords = CPAN PyPI distutils eggs package management
project_urls =
- Documentation = https://setuptools.readthedocs.io/
+ Documentation = https://setuptools.pypa.io/
[options]
packages = find_namespace:
# disabled as it causes tests to be included #2505
# include_package_data = true
-python_requires = >=3.6
+python_requires = >=3.7
install_requires =
[options.packages.find]
@@ -34,6 +34,7 @@ exclude =
docs*
tests*
*.tests
+ *.tests.*
tools*
[options.extras_require]
@@ -46,33 +47,49 @@ testing =
# workaround for jaraco/skeleton#22
python_implementation != "PyPy"
pytest-cov
- pytest-mypy; \
+ pytest-mypy >= 0.9.1; \
# workaround for jaraco/skeleton#22
python_implementation != "PyPy"
pytest-enabler >= 1.0.1
+ pytest-perf
# local
mock
flake8-2020
virtualenv>=13.0.0
- pytest-virtualenv>=1.2.7 # TODO: Update once man-group/pytest-plugins#188 is solved
wheel
- paver
pip>=19.1 # For proper file:// URLs support.
jaraco.envs>=2.2
pytest-xdist
- sphinx
+ sphinx>=4.3.2
+ jaraco.path>=3.2.0
+ build[virtualenv]
+ filelock>=3.4.0
+ pip_run>=8.8
+
+testing-integration =
+ pytest
+ pytest-xdist
+ pytest-enabler
+ virtualenv>=13.0.0
+ tomli
+ wheel
jaraco.path>=3.2.0
+ jaraco.envs>=2.2
+ build[virtualenv]
+ filelock>=3.4.0
+
docs =
# upstream
sphinx
- jaraco.packaging >= 8.2
+ jaraco.packaging >= 9
rst.linker >= 1.9
jaraco.tidelift >= 1.4
# local
pygments-github-lexers==0.0.5
+ sphinx-favicon
sphinx-inline-tabs
sphinxcontrib-towncrier
furo
diff --git a/setup.py b/setup.py
index c6affe97..4cda3d38 100755
--- a/setup.py
+++ b/setup.py
@@ -54,7 +54,7 @@ class install_with_pth(install):
_pth_contents = textwrap.dedent("""
import os
var = 'SETUPTOOLS_USE_DISTUTILS'
- enabled = os.environ.get(var, 'stdlib') == 'local'
+ enabled = os.environ.get(var, 'local') == 'local'
enabled and __import__('_distutils_hack').add_shim()
""").lstrip().replace('\n', '; ')
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index a623262e..06991b65 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -4,7 +4,6 @@ from fnmatch import fnmatchcase
import functools
import os
import re
-import warnings
import _distutils_hack.override # noqa: F401
@@ -19,6 +18,7 @@ from setuptools.extension import Extension
from setuptools.dist import Distribution
from setuptools.depends import Require
from . import monkey
+from . import logging
__all__ = [
@@ -132,7 +132,7 @@ def _install_setup_requires(attrs):
def __init__(self, attrs):
_incl = 'dependency_links', 'setup_requires'
filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
- distutils.core.Distribution.__init__(self, filtered)
+ super().__init__(filtered)
def finalize_options(self):
"""
@@ -145,16 +145,12 @@ def _install_setup_requires(attrs):
# Honor setup.cfg's options.
dist.parse_config_files(ignore_option_errors=True)
if dist.setup_requires:
- warnings.warn(
- "setup_requires is deprecated. Supply build "
- "dependencies using PEP 517 pyproject.toml build-requires.",
- SetuptoolsDeprecationWarning,
- )
dist.fetch_build_eggs(dist.setup_requires)
def setup(**attrs):
# Make sure we have any requirements needed to interpret 'attrs'.
+ logging.configure()
_install_setup_requires(attrs)
return distutils.core.setup(**attrs)
@@ -175,7 +171,7 @@ class Command(_Command):
Construct the command for dist, updating
vars(self) with any keyword parameters.
"""
- _Command.__init__(self, dist)
+ super().__init__(dist)
vars(self).update(kw)
def _ensure_stringlike(self, option, what, default=None):
diff --git a/setuptools/_distutils/__init__.py b/setuptools/_distutils/__init__.py
index 7dac55b6..8fd493b4 100644
--- a/setuptools/_distutils/__init__.py
+++ b/setuptools/_distutils/__init__.py
@@ -9,7 +9,16 @@ used from a setup script as
"""
import sys
+import importlib
__version__ = sys.version[:sys.version.index(' ')]
-local = True
+
+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/setuptools/_distutils/_collections.py b/setuptools/_distutils/_collections.py
new file mode 100644
index 00000000..98fce800
--- /dev/null
+++ b/setuptools/_distutils/_collections.py
@@ -0,0 +1,56 @@
+import collections
+import itertools
+
+
+# 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)))
diff --git a/setuptools/_distutils/_msvccompiler.py b/setuptools/_distutils/_msvccompiler.py
index b7a06082..f2f801c5 100644
--- a/setuptools/_distutils/_msvccompiler.py
+++ b/setuptools/_distutils/_msvccompiler.py
@@ -203,7 +203,7 @@ class MSVCCompiler(CCompiler) :
def __init__(self, verbose=0, dry_run=0, force=0):
- CCompiler.__init__ (self, verbose, dry_run, force)
+ super().__init__(verbose, dry_run, force)
# target platform (.plat_name is consistent with 'bdist')
self.plat_name = None
self.initialized = False
@@ -527,7 +527,7 @@ class MSVCCompiler(CCompiler) :
return
warnings.warn(
"Fallback spawn triggered. Please update distutils monkeypatch.")
- with unittest.mock.patch('os.environ', env):
+ with unittest.mock.patch.dict('os.environ', env):
bag.value = super().spawn(cmd)
# -- Miscellaneous methods -----------------------------------------
diff --git a/setuptools/_distutils/bcppcompiler.py b/setuptools/_distutils/bcppcompiler.py
index 071fea5d..2eb6d2e9 100644
--- a/setuptools/_distutils/bcppcompiler.py
+++ b/setuptools/_distutils/bcppcompiler.py
@@ -55,7 +55,7 @@ class BCPPCompiler(CCompiler) :
dry_run=0,
force=0):
- CCompiler.__init__ (self, verbose, dry_run, force)
+ 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
diff --git a/setuptools/_distutils/command/bdist_msi.py b/setuptools/_distutils/command/bdist_msi.py
index 0863a188..15259532 100644
--- a/setuptools/_distutils/command/bdist_msi.py
+++ b/setuptools/_distutils/command/bdist_msi.py
@@ -27,7 +27,7 @@ class PyDialog(Dialog):
def __init__(self, *args, **kw):
"""Dialog(database, name, x, y, w, h, attributes, title, first,
default, cancel, bitmap=true)"""
- Dialog.__init__(self, *args)
+ super().__init__(*args)
ruler = self.h - 36
bmwidth = 152*ruler/328
#if kw.get("bitmap", True):
diff --git a/setuptools/_distutils/command/build_ext.py b/setuptools/_distutils/command/build_ext.py
index 22628baf..181671bf 100644
--- a/setuptools/_distutils/command/build_ext.py
+++ b/setuptools/_distutils/command/build_ext.py
@@ -202,9 +202,7 @@ class build_ext(Command):
# 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()))
- _sys_home = getattr(sys, '_home', None)
- if _sys_home:
- self.library_dirs.append(_sys_home)
+ self.library_dirs.append(sys.base_exec_prefix)
# Use the .lib files for the correct architecture
if self.plat_name == 'win32':
diff --git a/setuptools/_distutils/command/check.py b/setuptools/_distutils/command/check.py
index ada25006..525540b6 100644
--- a/setuptools/_distutils/command/check.py
+++ b/setuptools/_distutils/command/check.py
@@ -17,7 +17,7 @@ try:
def __init__(self, source, report_level, halt_level, stream=None,
debug=0, encoding='ascii', error_handler='replace'):
self.messages = []
- Reporter.__init__(self, source, report_level, halt_level, stream,
+ super().__init__(source, report_level, halt_level, stream,
debug, encoding, error_handler)
def system_message(self, level, message, *children, **kwargs):
diff --git a/setuptools/_distutils/command/install.py b/setuptools/_distutils/command/install.py
index e98f0491..41c17d8a 100644
--- a/setuptools/_distutils/command/install.py
+++ b/setuptools/_distutils/command/install.py
@@ -4,6 +4,9 @@ Implements the Distutils 'install' command."""
import sys
import os
+import contextlib
+import sysconfig
+import itertools
from distutils import log
from distutils.core import Command
@@ -14,68 +17,69 @@ from distutils.file_util import write_file
from distutils.util import convert_path, subst_vars, change_root
from distutils.util import get_platform
from distutils.errors import DistutilsOptionError
+from .. import _collections
from site import USER_BASE
from site import USER_SITE
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',
+ 'purelib': '{base}/Lib/site-packages',
+ 'platlib': '{base}/Lib/site-packages',
+ 'headers': '{base}/Include/{dist_name}',
+ 'scripts': '{base}/Scripts',
+ 'data' : '{base}',
}
INSTALL_SCHEMES = {
- 'unix_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_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}',
},
- 'unix_home': {
- 'purelib': '$base/lib/$implementation_lower',
- 'platlib': '$base/$platlibdir/$implementation_lower',
- 'headers': '$base/include/$implementation_lower/$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',
+ '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',
+ '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/Include/$dist_name',
- 'scripts': '$userbase/$implementation$py_version_nodot/Scripts',
- 'data' : '$userbase',
+ '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['unix_user'] = {
- 'purelib': '$usersite',
- 'platlib': '$usersite',
+ INSTALL_SCHEMES['posix_user'] = {
+ 'purelib': '{usersite}',
+ 'platlib': '{usersite}',
'headers':
- '$userbase/include/$implementation_lower$py_version_short$abiflags/$dist_name',
- 'scripts': '$userbase/bin',
- 'data' : '$userbase',
+ '{userbase}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}',
+ 'scripts': '{userbase}/bin',
+ 'data' : '{userbase}',
}
# The keys to an installation scheme; if any new types of files are to be
@@ -83,6 +87,31 @@ if HAS_USER_SITE:
# 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'
@@ -90,6 +119,65 @@ def _get_implementation():
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 = _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"
@@ -284,7 +372,7 @@ class install(Command):
# 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_SCHEME dictionary above. Phew!
+ # install schemes. Phew!
self.dump_dirs("pre-finalize_{unix,other}")
@@ -307,25 +395,35 @@ class install(Command):
except AttributeError:
# sys.abiflags may not be defined on all platforms.
abiflags = ''
- self.config_vars = {'dist_name': self.distribution.get_name(),
- 'dist_version': self.distribution.get_version(),
- 'dist_fullname': self.distribution.get_fullname(),
- 'py_version': py_version,
- 'py_version_short': '%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(),
- }
+ 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:
- self.config_vars['userbase'] = self.install_userbase
- self.config_vars['usersite'] = self.install_usersite
+ local_vars['userbase'] = self.install_userbase
+ local_vars['usersite'] = self.install_usersite
+
+ self.config_vars = _collections.DictStack(
+ [compat_vars, sysconfig.get_config_vars(), local_vars])
self.expand_basedirs()
@@ -333,13 +431,13 @@ class install(Command):
# Now define config vars for the base directories so we can expand
# everything else.
- self.config_vars['base'] = self.install_base
- self.config_vars['platbase'] = self.install_platbase
+ local_vars['base'] = self.install_base
+ local_vars['platbase'] = self.install_platbase
if DEBUG:
from pprint import pprint
print("config vars:")
- pprint(self.config_vars)
+ pprint(dict(self.config_vars))
# Expand "~" and configuration variables in the installation
# directories.
@@ -415,12 +513,17 @@ class install(Command):
def finalize_unix(self):
"""Finalizes options for posix platforms."""
if self.install_base is not None or self.install_platbase is not None:
- if ((self.install_lib is None and
- self.install_purelib is None and
- self.install_platlib is None) or
+ 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):
+ self.install_data is None
+ )
+ if incomplete_scheme:
raise DistutilsOptionError(
"install-base or install-platbase supplied, but "
"installation scheme is incomplete")
@@ -431,18 +534,23 @@ class install(Command):
raise DistutilsPlatformError(
"User base directory is not specified")
self.install_base = self.install_platbase = self.install_userbase
- self.select_scheme("unix_user")
+ self.select_scheme("posix_user")
elif self.home is not None:
self.install_base = self.install_platbase = self.home
- self.select_scheme("unix_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")
- self.prefix = os.path.normpath(sys.prefix)
- self.exec_prefix = os.path.normpath(sys.exec_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:
@@ -450,7 +558,7 @@ class install(Command):
self.install_base = self.prefix
self.install_platbase = self.exec_prefix
- self.select_scheme("unix_prefix")
+ self.select_scheme("posix_prefix")
def finalize_other(self):
"""Finalizes options for non-posix platforms"""
@@ -462,7 +570,7 @@ class install(Command):
self.select_scheme(os.name + "_user")
elif self.home is not None:
self.install_base = self.install_platbase = self.home
- self.select_scheme("unix_home")
+ self.select_scheme("posix_home")
else:
if self.prefix is None:
self.prefix = os.path.normpath(sys.prefix)
@@ -475,20 +583,7 @@ class install(Command):
"I don't know how to install stuff on '%s'" % os.name)
def select_scheme(self, name):
- """Sets the install directories by applying the install schemes."""
- # it's the caller's problem if they supply a bad name!
- if (hasattr(sys, 'pypy_version_info') and
- sys.version_info < (3, 8) and
- not name.endswith(('_user', '_home'))):
- if os.name == 'nt':
- name = 'pypy_nt'
- else:
- name = 'pypy'
- scheme = INSTALL_SCHEMES[name]
- for key in SCHEME_KEYS:
- attrname = 'install_' + key
- if getattr(self, attrname) is None:
- setattr(self, attrname, scheme[key])
+ _select_scheme(self, name)
def _expand_attrs(self, attrs):
for attr in attrs:
@@ -562,7 +657,7 @@ class install(Command):
return
home = convert_path(os.path.expanduser("~"))
for name, path in self.config_vars.items():
- if path.startswith(home) and not os.path.isdir(path):
+ if str(path).startswith(home) and not os.path.isdir(path):
self.debug_print("os.makedirs('%s', 0o700)" % path)
os.makedirs(path, 0o700)
diff --git a/setuptools/_distutils/command/install_egg_info.py b/setuptools/_distutils/command/install_egg_info.py
index 0ddc7367..adc0323f 100644
--- a/setuptools/_distutils/command/install_egg_info.py
+++ b/setuptools/_distutils/command/install_egg_info.py
@@ -19,14 +19,21 @@ class install_egg_info(Command):
def initialize_options(self):
self.install_dir = None
- def finalize_options(self):
- self.set_undefined_options('install_lib',('install_dir','install_dir'))
- basename = "%s-%s-py%d.%d.egg-info" % (
+ @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]
)
- self.target = os.path.join(self.install_dir, basename)
+
+ 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):
diff --git a/setuptools/_distutils/core.py b/setuptools/_distutils/core.py
index d603d4a4..f43888ea 100644
--- a/setuptools/_distutils/core.py
+++ b/setuptools/_distutils/core.py
@@ -8,6 +8,7 @@ really defined in distutils.dist and distutils.cmd.
import os
import sys
+import tokenize
from distutils.debug import DEBUG
from distutils.errors import *
@@ -144,29 +145,41 @@ def setup (**attrs):
# And finally, run all the commands found on the command line.
if ok:
- try:
- dist.run_commands()
- except KeyboardInterrupt:
- raise SystemExit("interrupted")
- except OSError as exc:
- if DEBUG:
- sys.stderr.write("error: %s\n" % (exc,))
- raise
- else:
- raise SystemExit("error: %s" % (exc,))
-
- except (DistutilsError,
- CCompilerError) as msg:
- if DEBUG:
- raise
- else:
- raise SystemExit("error: " + str(msg))
+ 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("error: %s\n" % (exc,))
+ raise
+ else:
+ raise SystemExit("error: %s" % (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
@@ -205,14 +218,16 @@ def run_setup (script_name, script_args=None, stop_after="run"):
_setup_stop_after = stop_after
save_argv = sys.argv.copy()
- g = {'__file__': script_name}
+ g = {'__file__': script_name, '__name__': '__main__'}
try:
try:
sys.argv[0] = script_name
if script_args is not None:
sys.argv[1:] = script_args
- with open(script_name, 'rb') as f:
- exec(f.read(), g)
+ # 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
diff --git a/setuptools/_distutils/cygwinccompiler.py b/setuptools/_distutils/cygwinccompiler.py
index f1c38e39..c5c86d8f 100644
--- a/setuptools/_distutils/cygwinccompiler.py
+++ b/setuptools/_distutils/cygwinccompiler.py
@@ -50,15 +50,15 @@ cygwin in no-cygwin mode).
import os
import sys
import copy
-from subprocess import Popen, PIPE, check_output
-import re
+import shlex
+import warnings
+from subprocess import check_output
from distutils.unixccompiler import UnixCCompiler
from distutils.file_util import write_file
from distutils.errors import (DistutilsExecError, CCompilerError,
CompileError, UnknownFileError)
-from distutils.version import LooseVersion
-from distutils.spawn import find_executable
+from distutils.version import LooseVersion, suppress_known_deprecation
def get_msvcr():
"""Include the appropriate MSVC runtime library if Python was built
@@ -82,6 +82,15 @@ def get_msvcr():
elif msc_ver == '1600':
# VS2010 / MSVC 10.0
return ['msvcr100']
+ elif msc_ver == '1700':
+ # VS2012 / MSVC 11.0
+ return ['msvcr110']
+ elif msc_ver == '1800':
+ # VS2013 / MSVC 12.0
+ return ['msvcr120']
+ elif 1900 <= int(msc_ver) < 2000:
+ # VS2015 / MSVC 14.0
+ return ['ucrt', 'vcruntime140']
else:
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
@@ -99,7 +108,7 @@ class CygwinCCompiler(UnixCCompiler):
def __init__(self, verbose=0, dry_run=0, force=0):
- UnixCCompiler.__init__(self, verbose, dry_run, force)
+ super().__init__(verbose, dry_run, force)
status, details = check_config_h()
self.debug_print("Python's GCC status: %s (details: %s)" %
@@ -114,33 +123,8 @@ class CygwinCCompiler(UnixCCompiler):
self.cc = os.environ.get('CC', 'gcc')
self.cxx = os.environ.get('CXX', 'g++')
- if ('gcc' in self.cc): # Start gcc workaround
- self.gcc_version, self.ld_version, self.dllwrap_version = \
- get_versions()
- self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
- (self.gcc_version,
- self.ld_version,
- self.dllwrap_version) )
-
- # ld_version >= "2.10.90" and < "2.13" should also be able to use
- # gcc -mdll instead of dllwrap
- # Older dllwraps had own version numbers, newer ones use the
- # same as the rest of binutils ( also ld )
- # dllwrap 2.10.90 is buggy
- if self.ld_version >= "2.10.90":
- self.linker_dll = self.cc
- else:
- self.linker_dll = "dllwrap"
-
- # ld_version >= "2.13" support -shared so use it instead of
- # -mdll -static
- if self.ld_version >= "2.13":
- shared_option = "-shared"
- else:
- shared_option = "-mdll -static"
- else: # Assume linker is up to date
- self.linker_dll = self.cc
- shared_option = "-shared"
+ 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,
@@ -149,17 +133,24 @@ class CygwinCCompiler(UnixCCompiler):
linker_so=('%s -mcygwin %s' %
(self.linker_dll, shared_option)))
- # cygwin and mingw32 need different sets of libraries
- if ('gcc' in self.cc and self.gcc_version == "2.91.57"):
- # cygwin shouldn't need msvcrt, but without the dlls will crash
- # (gcc version 2.91.57) -- perhaps something about initialization
- self.dll_libraries=["msvcrt"]
- self.warn(
- "Consider upgrading to a newer version of gcc")
- else:
- # Include the appropriate MSVC runtime library if Python was built
- # with MSVC 7.0 or later.
- self.dll_libraries = get_msvcr()
+ # 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 dependend 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."""
@@ -221,24 +212,17 @@ class CygwinCCompiler(UnixCCompiler):
# next add options for def-file and to creating import libraries
- # dllwrap uses different options than gcc/ld
- if self.linker_dll == "dllwrap":
- extra_preargs.extend(["--output-lib", lib_file])
- # for dllwrap we have to use a special option
- extra_preargs.extend(["--def", def_file])
- # we use gcc/ld here and can be sure ld is >= 2.9.10
- else:
- # doesn't work: bfd_close build\...\libfoo.a: Invalid operation
- #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file])
- # for gcc/ld the def-file is specified as any object files
- objects.append(def_file)
+ # doesn't work: bfd_close build\...\libfoo.a: Invalid operation
+ #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_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 dllwrap/ld strip the output file
+ # 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))
@@ -284,21 +268,9 @@ class Mingw32CCompiler(CygwinCCompiler):
def __init__(self, verbose=0, dry_run=0, force=0):
- CygwinCCompiler.__init__ (self, verbose, dry_run, force)
-
- # ld_version >= "2.13" support -shared so use it instead of
- # -mdll -static
- if ('gcc' in self.cc and self.ld_version < "2.13"):
- shared_option = "-mdll -static"
- else:
- shared_option = "-shared"
+ super().__init__ (verbose, dry_run, force)
- # A real mingw32 doesn't need to specify a different entry point,
- # but cygwin 2.91.57 in no-cygwin-mode needs it.
- if ('gcc' in self.cc and self.gcc_version <= "2.91.57"):
- entry_point = '--entry _DllMain@12'
- else:
- entry_point = ''
+ shared_option = "-shared"
if is_cygwincc(self.cc):
raise CCompilerError(
@@ -308,9 +280,9 @@ class Mingw32CCompiler(CygwinCCompiler):
compiler_so='%s -mdll -O -Wall' % self.cc,
compiler_cxx='%s -O -Wall' % self.cxx,
linker_exe='%s' % self.cc,
- linker_so='%s %s %s'
- % (self.linker_dll, shared_option,
- entry_point))
+ linker_so='%s %s'
+ % (self.linker_dll, shared_option))
+
# Maybe we should also append -mthreads, but then the finished
# dlls need another dll (mingwm10.dll see Mingw32 docs)
# (-mthreads: Support thread-safe exception handling on `Mingw32')
@@ -377,38 +349,14 @@ def check_config_h():
return (CONFIG_H_UNCERTAIN,
"couldn't read '%s': %s" % (fn, exc.strerror))
-RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)')
-
-def _find_exe_version(cmd):
- """Find the version of an executable by running `cmd` in the shell.
-
- If the command is not found, or the output does not match
- `RE_VERSION`, returns None.
- """
- executable = cmd.split()[0]
- if find_executable(executable) is None:
- return None
- out = Popen(cmd, shell=True, stdout=PIPE).stdout
- try:
- out_string = out.read()
- finally:
- out.close()
- result = RE_VERSION.search(out_string)
- if result is None:
- return None
- # LooseVersion works with strings
- # so we need to decode our bytes
- return LooseVersion(result.group(1).decode())
-
-def get_versions():
- """ Try to find out the versions of gcc, ld and dllwrap.
-
- If not possible it returns None for it.
- """
- commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version']
- return tuple([_find_exe_version(cmd) for cmd in commands])
-
def is_cygwincc(cc):
'''Try to determine if the compiler that would be used is from cygwin.'''
- out_string = check_output([cc, '-dumpmachine'])
+ 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/setuptools/_distutils/log.py b/setuptools/_distutils/log.py
index 8ef6b28e..a68b156b 100644
--- a/setuptools/_distutils/log.py
+++ b/setuptools/_distutils/log.py
@@ -3,13 +3,14 @@
# The class here is styled after PEP 282 so that it could later be
# replaced with a standard Python logging implementation.
+import sys
+
DEBUG = 1
INFO = 2
WARN = 3
ERROR = 4
FATAL = 5
-import sys
class Log:
@@ -54,6 +55,7 @@ class Log:
def fatal(self, msg, *args):
self._log(FATAL, msg, args)
+
_global_log = Log()
log = _global_log.log
debug = _global_log.debug
@@ -62,12 +64,14 @@ warn = _global_log.warn
error = _global_log.error
fatal = _global_log.fatal
+
def set_threshold(level):
# return the old threshold for use from tests
old = _global_log.threshold
_global_log.threshold = level
return old
+
def set_verbosity(v):
if v <= 0:
set_threshold(WARN)
diff --git a/setuptools/_distutils/msvc9compiler.py b/setuptools/_distutils/msvc9compiler.py
index a1b3b02f..6b627383 100644
--- a/setuptools/_distutils/msvc9compiler.py
+++ b/setuptools/_distutils/msvc9compiler.py
@@ -291,8 +291,6 @@ def query_vcvarsall(version, arch="x86"):
# More globals
VERSION = get_build_version()
-if VERSION < 8.0:
- raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
# MACROS = MacroExpander(VERSION)
class MSVCCompiler(CCompiler) :
@@ -326,7 +324,7 @@ class MSVCCompiler(CCompiler) :
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=0, force=0):
- CCompiler.__init__ (self, verbose, dry_run, force)
+ super().__init__(verbose, dry_run, force)
self.__version = VERSION
self.__root = r"Software\Microsoft\VisualStudio"
# self.__macros = MACROS
@@ -339,6 +337,8 @@ class MSVCCompiler(CCompiler) :
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 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.
diff --git a/setuptools/_distutils/msvccompiler.py b/setuptools/_distutils/msvccompiler.py
index 2d447b85..e1367b89 100644
--- a/setuptools/_distutils/msvccompiler.py
+++ b/setuptools/_distutils/msvccompiler.py
@@ -228,7 +228,7 @@ class MSVCCompiler(CCompiler) :
exe_extension = '.exe'
def __init__(self, verbose=0, dry_run=0, force=0):
- CCompiler.__init__ (self, verbose, dry_run, force)
+ super().__init__(verbose, dry_run, force)
self.__version = get_build_version()
self.__arch = get_build_architecture()
if self.__arch == "Intel":
diff --git a/setuptools/_distutils/spawn.py b/setuptools/_distutils/spawn.py
index 6e1c89f1..b2d10e39 100644
--- a/setuptools/_distutils/spawn.py
+++ b/setuptools/_distutils/spawn.py
@@ -10,7 +10,7 @@ import sys
import os
import subprocess
-from distutils.errors import DistutilsPlatformError, DistutilsExecError
+from distutils.errors import DistutilsExecError
from distutils.debug import DEBUG
from distutils import log
diff --git a/setuptools/_distutils/sysconfig.py b/setuptools/_distutils/sysconfig.py
index 8832b3ec..4a77a431 100644
--- a/setuptools/_distutils/sysconfig.py
+++ b/setuptools/_distutils/sysconfig.py
@@ -13,6 +13,7 @@ import _imp
import os
import re
import sys
+import sysconfig
from .errors import DistutilsPlatformError
@@ -129,6 +130,14 @@ def get_python_inc(plat_specific=0, prefix=None):
"on platform '%s'" % os.name)
+# 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).
@@ -152,6 +161,8 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
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
@@ -169,10 +180,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
implementation = 'pypy' if IS_PYPY else 'python'
libpython = os.path.join(prefix, libdir,
implementation + get_python_version())
- if standard_lib:
- return libpython
- else:
- return os.path.join(libpython, "site-packages")
+ return _posix_lib(standard_lib, libpython, early_prefix, prefix)
elif os.name == "nt":
if standard_lib:
return os.path.join(prefix, "Lib")
@@ -267,21 +275,15 @@ def get_config_h_filename():
inc_dir = os.path.join(_sys_home or project_base, "PC")
else:
inc_dir = _sys_home or project_base
+ return os.path.join(inc_dir, 'pyconfig.h')
else:
- inc_dir = get_python_inc(plat_specific=1)
+ return sysconfig.get_config_h_filename()
- return os.path.join(inc_dir, 'pyconfig.h')
def get_makefile_filename():
"""Return full pathname of installed Makefile from the Python build."""
- if python_build:
- return os.path.join(_sys_home or project_base, "Makefile")
- lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
- config_file = 'config-{}{}'.format(get_python_version(), build_flags)
- if hasattr(sys.implementation, '_multiarch'):
- config_file += '-%s' % sys.implementation._multiarch
- return os.path.join(lib_dir, config_file, 'Makefile')
+ return sysconfig.get_makefile_filename()
def parse_config_h(fp, g=None):
@@ -291,26 +293,7 @@ def parse_config_h(fp, g=None):
optional dictionary is passed in as the second argument, it is
used instead of a new dictionary.
"""
- if g is None:
- g = {}
- define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
- undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
- #
- while True:
- line = fp.readline()
- if not line:
- break
- m = define_rx.match(line)
- if m:
- n, v = m.group(1, 2)
- try: v = int(v)
- except ValueError: pass
- g[n] = v
- else:
- m = undef_rx.match(line)
- if m:
- g[m.group(1)] = 0
- return g
+ return sysconfig.parse_config_h(fp, vars=g)
# Regexes needed for parsing Makefile (and similar syntaxes,
@@ -452,15 +435,21 @@ def expand_makefile_vars(s, vars):
_config_vars = None
+
+_sysconfig_name_tmpl = '_sysconfigdata_{abi}_{platform}_{multiarch}'
+
+
def _init_posix():
"""Initialize the module as appropriate for POSIX systems."""
# _sysconfigdata is generated at build time, see the sysconfig module
- name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
- '_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
- abi=sys.abiflags,
- platform=sys.platform,
- multiarch=getattr(sys.implementation, '_multiarch', ''),
- ))
+ name = os.environ.get(
+ '_PYTHON_SYSCONFIGDATA_NAME',
+ _sysconfig_name_tmpl.format(
+ abi=sys.abiflags,
+ platform=sys.platform,
+ multiarch=getattr(sys.implementation, '_multiarch', ''),
+ ),
+ )
try:
_temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
except ImportError:
diff --git a/setuptools/_distutils/tests/py38compat.py b/setuptools/_distutils/tests/py38compat.py
index 32269c7b..c949f58e 100644
--- a/setuptools/_distutils/tests/py38compat.py
+++ b/setuptools/_distutils/tests/py38compat.py
@@ -2,6 +2,11 @@
import contextlib
import builtins
+import sys
+
+from test.support import requires_zlib
+import test.support
+
ModuleNotFoundError = getattr(builtins, 'ModuleNotFoundError', ImportError)
@@ -51,3 +56,7 @@ try:
from test.support.warnings_helper import save_restore_warnings_filters
except (ModuleNotFoundError, ImportError):
save_restore_warnings_filters = _save_restore_warnings_filters
+
+
+if sys.version_info < (3, 9):
+ requires_zlib = lambda: test.support.requires_zlib
diff --git a/setuptools/_distutils/tests/test_archive_util.py b/setuptools/_distutils/tests/test_archive_util.py
index ce6456dc..800b9018 100644
--- a/setuptools/_distutils/tests/test_archive_util.py
+++ b/setuptools/_distutils/tests/test_archive_util.py
@@ -14,16 +14,11 @@ from distutils.archive_util import (check_archive_formats, make_tarball,
from distutils.spawn import find_executable, spawn
from distutils.tests import support
from test.support import run_unittest, patch
+from .unix_compat import require_unix_id, require_uid_0, grp, pwd, UID_0_SUPPORT
from .py38compat import change_cwd
from .py38compat import check_warnings
-try:
- import grp
- import pwd
- UID_GID_SUPPORT = True
-except ImportError:
- UID_GID_SUPPORT = False
try:
import zipfile
@@ -339,7 +334,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
def test_make_archive_owner_group(self):
# testing make_archive with owner and group, with various combinations
# this works even if there's not gid/uid support
- if UID_GID_SUPPORT:
+ if UID_0_SUPPORT:
group = grp.getgrgid(0)[0]
owner = pwd.getpwuid(0)[0]
else:
@@ -364,7 +359,8 @@ class ArchiveUtilTestCase(support.TempdirManager,
self.assertTrue(os.path.exists(res))
@unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib")
- @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ @require_unix_id
+ @require_uid_0
def test_tarfile_root_owner(self):
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
@@ -391,7 +387,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
archive.close()
def test_suite():
- return unittest.makeSuite(ArchiveUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(ArchiveUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist.py b/setuptools/_distutils/tests/test_bdist.py
index 130d8bf1..8b7498e3 100644
--- a/setuptools/_distutils/tests/test_bdist.py
+++ b/setuptools/_distutils/tests/test_bdist.py
@@ -51,7 +51,7 @@ class BuildTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(BuildTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_dumb.py b/setuptools/_distutils/tests/test_bdist_dumb.py
index 01a233bc..bb860c8a 100644
--- a/setuptools/_distutils/tests/test_bdist_dumb.py
+++ b/setuptools/_distutils/tests/test_bdist_dumb.py
@@ -91,7 +91,7 @@ class BuildDumbTestCase(support.TempdirManager,
self.assertEqual(contents, sorted(wanted))
def test_suite():
- return unittest.makeSuite(BuildDumbTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildDumbTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_msi.py b/setuptools/_distutils/tests/test_bdist_msi.py
index 937266f8..b1831ef2 100644
--- a/setuptools/_distutils/tests/test_bdist_msi.py
+++ b/setuptools/_distutils/tests/test_bdist_msi.py
@@ -22,7 +22,7 @@ class BDistMSITestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(BDistMSITestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BDistMSITestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_rpm.py b/setuptools/_distutils/tests/test_bdist_rpm.py
index 6453a02b..08a7cb46 100644
--- a/setuptools/_distutils/tests/test_bdist_rpm.py
+++ b/setuptools/_distutils/tests/test_bdist_rpm.py
@@ -3,13 +3,16 @@
import unittest
import sys
import os
-from test.support import run_unittest, requires_zlib
+from test.support import run_unittest
from distutils.core import Distribution
from distutils.command.bdist_rpm import bdist_rpm
from distutils.tests import support
from distutils.spawn import find_executable
+from .py38compat import requires_zlib
+
+
SETUP_PY = """\
from distutils.core import setup
import foo
@@ -44,7 +47,7 @@ class BuildRpmTestCase(support.TempdirManager,
# spurious sdtout/stderr output under Mac OS X
@unittest.skipUnless(sys.platform.startswith('linux'),
'spurious sdtout/stderr output under Mac OS X')
- @requires_zlib
+ @requires_zlib()
@unittest.skipIf(find_executable('rpm') is None,
'the rpm command is not found')
@unittest.skipIf(find_executable('rpmbuild') is None,
@@ -87,7 +90,7 @@ class BuildRpmTestCase(support.TempdirManager,
# spurious sdtout/stderr output under Mac OS X
@unittest.skipUnless(sys.platform.startswith('linux'),
'spurious sdtout/stderr output under Mac OS X')
- @requires_zlib
+ @requires_zlib()
# http://bugs.python.org/issue1533164
@unittest.skipIf(find_executable('rpm') is None,
'the rpm command is not found')
@@ -129,7 +132,7 @@ class BuildRpmTestCase(support.TempdirManager,
os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm'))
def test_suite():
- return unittest.makeSuite(BuildRpmTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildRpmTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_wininst.py b/setuptools/_distutils/tests/test_bdist_wininst.py
index 31cf2628..59f25167 100644
--- a/setuptools/_distutils/tests/test_bdist_wininst.py
+++ b/setuptools/_distutils/tests/test_bdist_wininst.py
@@ -34,7 +34,7 @@ class BuildWinInstTestCase(support.TempdirManager,
self.assertGreater(len(exe_file), 10)
def test_suite():
- return unittest.makeSuite(BuildWinInstTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildWinInstTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build.py b/setuptools/_distutils/tests/test_build.py
index b020a5ba..83a9e4f4 100644
--- a/setuptools/_distutils/tests/test_build.py
+++ b/setuptools/_distutils/tests/test_build.py
@@ -50,7 +50,7 @@ class BuildTestCase(support.TempdirManager,
self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
def test_suite():
- return unittest.makeSuite(BuildTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build_clib.py b/setuptools/_distutils/tests/test_build_clib.py
index 259c4352..d50ead7c 100644
--- a/setuptools/_distutils/tests/test_build_clib.py
+++ b/setuptools/_distutils/tests/test_build_clib.py
@@ -130,7 +130,7 @@ class BuildCLibTestCase(support.TempdirManager,
self.assertIn('libfoo.a', os.listdir(build_temp))
def test_suite():
- return unittest.makeSuite(BuildCLibTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildCLibTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build_ext.py b/setuptools/_distutils/tests/test_build_ext.py
index 85ecf4b7..920e4dc8 100644
--- a/setuptools/_distutils/tests/test_build_ext.py
+++ b/setuptools/_distutils/tests/test_build_ext.py
@@ -493,12 +493,16 @@ class BuildExtTestCase(TempdirManager,
# format the target value as defined in the Apple
# Availability Macros. We can't use the macro names since
# at least one value we test with will not exist yet.
- if target[1] < 10:
+ if target[:2] < (10, 10):
# for 10.1 through 10.9.x -> "10n0"
target = '%02d%01d0' % target
else:
# for 10.10 and beyond -> "10nn00"
- target = '%02d%02d00' % target
+ if len(target) >= 2:
+ target = '%02d%02d00' % target
+ else:
+ # 11 and later can have no minor version (11 instead of 11.0)
+ target = '%02d0000' % target
deptarget_ext = Extension(
'deptarget',
[deptarget_c],
@@ -538,8 +542,8 @@ class ParallelBuildExtTestCase(BuildExtTestCase):
def test_suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(BuildExtTestCase))
- suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(BuildExtTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(ParallelBuildExtTestCase))
return suite
if __name__ == '__main__':
diff --git a/setuptools/_distutils/tests/test_build_py.py b/setuptools/_distutils/tests/test_build_py.py
index 0712e92c..a590a485 100644
--- a/setuptools/_distutils/tests/test_build_py.py
+++ b/setuptools/_distutils/tests/test_build_py.py
@@ -173,7 +173,7 @@ class BuildPyTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(BuildPyTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildPyTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build_scripts.py b/setuptools/_distutils/tests/test_build_scripts.py
index 954fc763..f299e51e 100644
--- a/setuptools/_distutils/tests/test_build_scripts.py
+++ b/setuptools/_distutils/tests/test_build_scripts.py
@@ -106,7 +106,7 @@ class BuildScriptsTestCase(support.TempdirManager,
self.assertIn(name, built)
def test_suite():
- return unittest.makeSuite(BuildScriptsTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildScriptsTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_check.py b/setuptools/_distutils/tests/test_check.py
index e534aca1..91bcdceb 100644
--- a/setuptools/_distutils/tests/test_check.py
+++ b/setuptools/_distutils/tests/test_check.py
@@ -157,7 +157,7 @@ class CheckTestCase(support.LoggingSilencer,
'restructuredtext': 1})
def test_suite():
- return unittest.makeSuite(CheckTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CheckTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_clean.py b/setuptools/_distutils/tests/test_clean.py
index c605afd8..92367499 100644
--- a/setuptools/_distutils/tests/test_clean.py
+++ b/setuptools/_distutils/tests/test_clean.py
@@ -43,7 +43,7 @@ class cleanTestCase(support.TempdirManager,
cmd.run()
def test_suite():
- return unittest.makeSuite(cleanTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(cleanTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_cmd.py b/setuptools/_distutils/tests/test_cmd.py
index cf5197c3..2319214a 100644
--- a/setuptools/_distutils/tests/test_cmd.py
+++ b/setuptools/_distutils/tests/test_cmd.py
@@ -120,7 +120,7 @@ class CommandTestCase(unittest.TestCase):
debug.DEBUG = False
def test_suite():
- return unittest.makeSuite(CommandTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CommandTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_config.py b/setuptools/_distutils/tests/test_config.py
index 344084af..27bd9d44 100644
--- a/setuptools/_distutils/tests/test_config.py
+++ b/setuptools/_distutils/tests/test_config.py
@@ -66,7 +66,7 @@ class BasePyPIRCCommandTestCase(support.TempdirManager,
class command(PyPIRCCommand):
def __init__(self, dist):
- PyPIRCCommand.__init__(self, dist)
+ super().__init__(dist)
def initialize_options(self):
pass
finalize_options = initialize_options
@@ -135,7 +135,7 @@ class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase):
def test_suite():
- return unittest.makeSuite(PyPIRCCommandTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(PyPIRCCommandTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_config_cmd.py b/setuptools/_distutils/tests/test_config_cmd.py
index 4cd9a6b9..2c84719a 100644
--- a/setuptools/_distutils/tests/test_config_cmd.py
+++ b/setuptools/_distutils/tests/test_config_cmd.py
@@ -92,7 +92,7 @@ class ConfigTestCase(support.LoggingSilencer,
self.assertFalse(os.path.exists(f))
def test_suite():
- return unittest.makeSuite(ConfigTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(ConfigTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_core.py b/setuptools/_distutils/tests/test_core.py
index 666ff4a3..7270d699 100644
--- a/setuptools/_distutils/tests/test_core.py
+++ b/setuptools/_distutils/tests/test_core.py
@@ -10,6 +10,7 @@ from . import py38compat as os_helper
import unittest
from distutils.tests import support
from distutils import log
+from distutils.dist import Distribution
# setup script that uses __file__
setup_using___file__ = """\
@@ -45,6 +46,16 @@ class install(_install):
setup(cmdclass={'install': install})
"""
+setup_within_if_main = """\
+from distutils.core import setup
+
+def main():
+ return setup(name="setup_within_if_main")
+
+if __name__ == "__main__":
+ main()
+"""
+
class CoreTestCase(support.EnvironGuard, unittest.TestCase):
def setUp(self):
@@ -115,6 +126,20 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase):
output = output[:-1]
self.assertEqual(cwd, output)
+ def test_run_setup_within_if_main(self):
+ dist = distutils.core.run_setup(
+ self.write_setup(setup_within_if_main), stop_after="config")
+ self.assertIsInstance(dist, Distribution)
+ self.assertEqual(dist.get_name(), "setup_within_if_main")
+
+ def test_run_commands(self):
+ sys.argv = ['setup.py', 'build']
+ dist = distutils.core.run_setup(
+ self.write_setup(setup_within_if_main), stop_after="commandline")
+ self.assertNotIn('build', dist.have_run)
+ distutils.core.run_commands(dist)
+ self.assertIn('build', dist.have_run)
+
def test_debug_mode(self):
# this covers the code called when DEBUG is set
sys.argv = ['setup.py', '--name']
@@ -134,7 +159,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase):
self.assertEqual(stdout.readlines()[0], wanted)
def test_suite():
- return unittest.makeSuite(CoreTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CoreTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_cygwinccompiler.py b/setuptools/_distutils/tests/test_cygwinccompiler.py
index 9dc869de..8715a535 100644
--- a/setuptools/_distutils/tests/test_cygwinccompiler.py
+++ b/setuptools/_distutils/tests/test_cygwinccompiler.py
@@ -2,28 +2,14 @@
import unittest
import sys
import os
-from io import BytesIO
from test.support import run_unittest
-from distutils import cygwinccompiler
from distutils.cygwinccompiler import (check_config_h,
CONFIG_H_OK, CONFIG_H_NOTOK,
- CONFIG_H_UNCERTAIN, get_versions,
+ CONFIG_H_UNCERTAIN,
get_msvcr)
from distutils.tests import support
-class FakePopen(object):
- test_class = None
-
- def __init__(self, cmd, shell, stdout):
- self.cmd = cmd.split()[0]
- exes = self.test_class._exes
- if self.cmd in exes:
- # issue #6438 in Python 3.x, Popen returns bytes
- self.stdout = BytesIO(exes[self.cmd])
- else:
- self.stdout = os.popen(cmd, 'r')
-
class CygwinCCompilerTestCase(support.TempdirManager,
unittest.TestCase):
@@ -35,29 +21,16 @@ class CygwinCCompilerTestCase(support.TempdirManager,
from distutils import sysconfig
self.old_get_config_h_filename = sysconfig.get_config_h_filename
sysconfig.get_config_h_filename = self._get_config_h_filename
- self.old_find_executable = cygwinccompiler.find_executable
- cygwinccompiler.find_executable = self._find_executable
- self._exes = {}
- self.old_popen = cygwinccompiler.Popen
- FakePopen.test_class = self
- cygwinccompiler.Popen = FakePopen
def tearDown(self):
sys.version = self.version
from distutils import sysconfig
sysconfig.get_config_h_filename = self.old_get_config_h_filename
- cygwinccompiler.find_executable = self.old_find_executable
- cygwinccompiler.Popen = self.old_popen
super(CygwinCCompilerTestCase, self).tearDown()
def _get_config_h_filename(self):
return self.python_h
- def _find_executable(self, name):
- if name in self._exes:
- return name
- return None
-
def test_check_config_h(self):
# check_config_h looks for "GCC" in sys.version first
@@ -81,40 +54,6 @@ class CygwinCCompilerTestCase(support.TempdirManager,
self.write_file(self.python_h, 'xxx __GNUC__ xxx')
self.assertEqual(check_config_h()[0], CONFIG_H_OK)
- def test_get_versions(self):
-
- # get_versions calls distutils.spawn.find_executable on
- # 'gcc', 'ld' and 'dllwrap'
- self.assertEqual(get_versions(), (None, None, None))
-
- # Let's fake we have 'gcc' and it returns '3.4.5'
- self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF'
- res = get_versions()
- self.assertEqual(str(res[0]), '3.4.5')
-
- # and let's see what happens when the version
- # doesn't match the regular expression
- # (\d+\.\d+(\.\d+)*)
- self._exes['gcc'] = b'very strange output'
- res = get_versions()
- self.assertEqual(res[0], None)
-
- # same thing for ld
- self._exes['ld'] = b'GNU ld version 2.17.50 20060824'
- res = get_versions()
- self.assertEqual(str(res[1]), '2.17.50')
- self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77'
- res = get_versions()
- self.assertEqual(res[1], None)
-
- # and dllwrap
- self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF'
- res = get_versions()
- self.assertEqual(str(res[2]), '2.17.50')
- self._exes['dllwrap'] = b'Cheese Wrap'
- res = get_versions()
- self.assertEqual(res[2], None)
-
def test_get_msvcr(self):
# none
@@ -141,14 +80,17 @@ class CygwinCCompilerTestCase(support.TempdirManager,
sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
'[MSC v.1500 32 bits (Intel)]')
self.assertEqual(get_msvcr(), ['msvcr90'])
+
+ sys.version = '3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 18:46:30) [MSC v.1929 32 bit (Intel)]'
+ self.assertEqual(get_msvcr(), ['ucrt', 'vcruntime140'])
# unknown
sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
- '[MSC v.1999 32 bits (Intel)]')
+ '[MSC v.2000 32 bits (Intel)]')
self.assertRaises(ValueError, get_msvcr)
def test_suite():
- return unittest.makeSuite(CygwinCCompilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CygwinCCompilerTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_dep_util.py b/setuptools/_distutils/tests/test_dep_util.py
index c6fae39c..0d52740a 100644
--- a/setuptools/_distutils/tests/test_dep_util.py
+++ b/setuptools/_distutils/tests/test_dep_util.py
@@ -74,7 +74,7 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(DepUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(DepUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_dir_util.py b/setuptools/_distutils/tests/test_dir_util.py
index d436cf83..1b1f3bbb 100644
--- a/setuptools/_distutils/tests/test_dir_util.py
+++ b/setuptools/_distutils/tests/test_dir_util.py
@@ -133,7 +133,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(DirUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(DirUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_dist.py b/setuptools/_distutils/tests/test_dist.py
index 45eadee8..36155be1 100644
--- a/setuptools/_distutils/tests/test_dist.py
+++ b/setuptools/_distutils/tests/test_dist.py
@@ -525,8 +525,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
def test_suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(DistributionTestCase))
- suite.addTest(unittest.makeSuite(MetadataTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(DistributionTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(MetadataTestCase))
return suite
if __name__ == "__main__":
diff --git a/setuptools/_distutils/tests/test_extension.py b/setuptools/_distutils/tests/test_extension.py
index 2eb5b422..78a55daa 100644
--- a/setuptools/_distutils/tests/test_extension.py
+++ b/setuptools/_distutils/tests/test_extension.py
@@ -65,7 +65,7 @@ class ExtensionTestCase(unittest.TestCase):
"Unknown Extension options: 'chic'")
def test_suite():
- return unittest.makeSuite(ExtensionTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(ExtensionTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_file_util.py b/setuptools/_distutils/tests/test_file_util.py
index d2536075..81b90d6c 100644
--- a/setuptools/_distutils/tests/test_file_util.py
+++ b/setuptools/_distutils/tests/test_file_util.py
@@ -118,7 +118,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(FileUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(FileUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_filelist.py b/setuptools/_distutils/tests/test_filelist.py
index 9ec507b5..a90edcf1 100644
--- a/setuptools/_distutils/tests/test_filelist.py
+++ b/setuptools/_distutils/tests/test_filelist.py
@@ -344,8 +344,8 @@ class FindAllTestCase(unittest.TestCase):
def test_suite():
return unittest.TestSuite([
- unittest.makeSuite(FileListTestCase),
- unittest.makeSuite(FindAllTestCase),
+ unittest.TestLoader().loadTestsFromTestCase(FileListTestCase),
+ unittest.TestLoader().loadTestsFromTestCase(FindAllTestCase),
])
diff --git a/setuptools/_distutils/tests/test_install.py b/setuptools/_distutils/tests/test_install.py
index eb684a09..5dbc06b0 100644
--- a/setuptools/_distutils/tests/test_install.py
+++ b/setuptools/_distutils/tests/test_install.py
@@ -81,7 +81,9 @@ class InstallTestCase(support.TempdirManager,
install_module.USER_SITE = self.user_site
def _expanduser(path):
- return self.tmpdir
+ if path.startswith('~'):
+ return os.path.normpath(self.tmpdir + path[1:])
+ return path
self.old_expand = os.path.expanduser
os.path.expanduser = _expanduser
@@ -94,7 +96,7 @@ class InstallTestCase(support.TempdirManager,
self.addCleanup(cleanup)
- for key in ('nt_user', 'unix_user'):
+ for key in ('nt_user', 'posix_user'):
self.assertIn(key, INSTALL_SCHEMES)
dist = Distribution({'name': 'xx'})
@@ -122,6 +124,17 @@ class InstallTestCase(support.TempdirManager,
self.assertIn('userbase', cmd.config_vars)
self.assertIn('usersite', cmd.config_vars)
+ actual_headers = os.path.relpath(cmd.install_headers, self.user_base)
+ if os.name == 'nt':
+ site_path = os.path.relpath(
+ os.path.dirname(self.old_user_site), self.old_user_base)
+ include = os.path.join(site_path, 'Include')
+ else:
+ include = sysconfig.get_python_inc(0, '')
+ expect_headers = os.path.join(include, 'xx')
+
+ self.assertEqual(os.path.normcase(actual_headers), os.path.normcase(expect_headers))
+
def test_handle_extra_path(self):
dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
cmd = install(dist)
@@ -244,7 +257,7 @@ class InstallTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(InstallTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_data.py b/setuptools/_distutils/tests/test_install_data.py
index 32ab296a..6191d2fa 100644
--- a/setuptools/_distutils/tests/test_install_data.py
+++ b/setuptools/_distutils/tests/test_install_data.py
@@ -69,7 +69,7 @@ class InstallDataTestCase(support.TempdirManager,
self.assertTrue(os.path.exists(os.path.join(inst, rone)))
def test_suite():
- return unittest.makeSuite(InstallDataTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallDataTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_headers.py b/setuptools/_distutils/tests/test_install_headers.py
index 2217b321..1aa4d09c 100644
--- a/setuptools/_distutils/tests/test_install_headers.py
+++ b/setuptools/_distutils/tests/test_install_headers.py
@@ -33,7 +33,7 @@ class InstallHeadersTestCase(support.TempdirManager,
self.assertEqual(len(cmd.get_outputs()), 2)
def test_suite():
- return unittest.makeSuite(InstallHeadersTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallHeadersTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_lib.py b/setuptools/_distutils/tests/test_install_lib.py
index fda6315b..652653f2 100644
--- a/setuptools/_distutils/tests/test_install_lib.py
+++ b/setuptools/_distutils/tests/test_install_lib.py
@@ -109,7 +109,7 @@ class InstallLibTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(InstallLibTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallLibTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_scripts.py b/setuptools/_distutils/tests/test_install_scripts.py
index 1f7b1038..648db3b1 100644
--- a/setuptools/_distutils/tests/test_install_scripts.py
+++ b/setuptools/_distutils/tests/test_install_scripts.py
@@ -76,7 +76,7 @@ class InstallScriptsTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(InstallScriptsTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallScriptsTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_log.py b/setuptools/_distutils/tests/test_log.py
index 75cf9006..ec2ae028 100644
--- a/setuptools/_distutils/tests/test_log.py
+++ b/setuptools/_distutils/tests/test_log.py
@@ -40,7 +40,7 @@ class TestLog(unittest.TestCase):
'Fαtal\t\\xc8rr\\u014dr')
def test_suite():
- return unittest.makeSuite(TestLog)
+ return unittest.TestLoader().loadTestsFromTestCase(TestLog)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_msvc9compiler.py b/setuptools/_distutils/tests/test_msvc9compiler.py
index 77a07ef3..6235405e 100644
--- a/setuptools/_distutils/tests/test_msvc9compiler.py
+++ b/setuptools/_distutils/tests/test_msvc9compiler.py
@@ -178,7 +178,7 @@ class msvc9compilerTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(msvc9compilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(msvc9compilerTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_msvccompiler.py b/setuptools/_distutils/tests/test_msvccompiler.py
index 46a51cd0..846e5bb8 100644
--- a/setuptools/_distutils/tests/test_msvccompiler.py
+++ b/setuptools/_distutils/tests/test_msvccompiler.py
@@ -98,7 +98,7 @@ class TestSpawn(unittest.TestCase):
compiler = _msvccompiler.MSVCCompiler()
compiler._paths = "expected"
inner_cmd = 'import os; assert os.environ["PATH"] == "expected"'
- command = ['python', '-c', inner_cmd]
+ command = [sys.executable, '-c', inner_cmd]
threads = [
CheckThread(target=compiler.spawn, args=[command])
@@ -132,7 +132,7 @@ class TestSpawn(unittest.TestCase):
def test_suite():
- return unittest.makeSuite(msvccompilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(msvccompilerTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_register.py b/setuptools/_distutils/tests/test_register.py
index 84607f99..5770ed58 100644
--- a/setuptools/_distutils/tests/test_register.py
+++ b/setuptools/_distutils/tests/test_register.py
@@ -319,7 +319,7 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
def test_suite():
- return unittest.makeSuite(RegisterTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(RegisterTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_sdist.py b/setuptools/_distutils/tests/test_sdist.py
index b087a817..4c51717c 100644
--- a/setuptools/_distutils/tests/test_sdist.py
+++ b/setuptools/_distutils/tests/test_sdist.py
@@ -7,6 +7,7 @@ import zipfile
from os.path import join
from textwrap import dedent
from test.support import captured_stdout, run_unittest
+from .unix_compat import require_unix_id, require_uid_0, pwd, grp
from .py38compat import check_warnings
@@ -16,13 +17,6 @@ try:
except ImportError:
ZLIB_SUPPORT = False
-try:
- import grp
- import pwd
- UID_GID_SUPPORT = True
-except ImportError:
- UID_GID_SUPPORT = False
-
from distutils.command.sdist import sdist, show_formats
from distutils.core import Distribution
from distutils.tests.test_config import BasePyPIRCCommandTestCase
@@ -440,7 +434,8 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
'fake-1.0/README.manual'])
@unittest.skipUnless(ZLIB_SUPPORT, "requires zlib")
- @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ @require_unix_id
+ @require_uid_0
@unittest.skipIf(find_executable('tar') is None,
"The tar command is not found")
@unittest.skipIf(find_executable('gzip') is None,
@@ -488,7 +483,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
archive.close()
def test_suite():
- return unittest.makeSuite(SDistTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(SDistTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_spawn.py b/setuptools/_distutils/tests/test_spawn.py
index f620da78..c5ed8e2b 100644
--- a/setuptools/_distutils/tests/test_spawn.py
+++ b/setuptools/_distutils/tests/test_spawn.py
@@ -133,7 +133,7 @@ class SpawnTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(SpawnTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(SpawnTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_sysconfig.py b/setuptools/_distutils/tests/test_sysconfig.py
index 80cd1599..9de3cb70 100644
--- a/setuptools/_distutils/tests/test_sysconfig.py
+++ b/setuptools/_distutils/tests/test_sysconfig.py
@@ -38,6 +38,12 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
config_h = sysconfig.get_config_h_filename()
self.assertTrue(os.path.isfile(config_h), config_h)
+ @unittest.skipIf(sys.platform == 'win32',
+ 'Makefile only exists on Unix like systems')
+ def test_get_makefile_filename(self):
+ makefile = sysconfig.get_makefile_filename()
+ self.assertTrue(os.path.isfile(makefile), makefile)
+
def test_get_python_lib(self):
# XXX doesn't work on Linux when Python was never installed before
#self.assertTrue(os.path.isdir(lib_dir), lib_dir)
@@ -283,10 +289,19 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
outs, errs = p.communicate()
self.assertEqual(0, p.returncode, "Subprocess failed: " + outs)
+ def test_parse_config_h(self):
+ config_h = sysconfig.get_config_h_filename()
+ input = {}
+ with open(config_h, encoding="utf-8") as f:
+ result = sysconfig.parse_config_h(f, g=input)
+ self.assertTrue(input is result)
+ with open(config_h, encoding="utf-8") as f:
+ result = sysconfig.parse_config_h(f)
+ self.assertTrue(isinstance(result, dict))
def test_suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(SysconfigTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(SysconfigTestCase))
return suite
diff --git a/setuptools/_distutils/tests/test_text_file.py b/setuptools/_distutils/tests/test_text_file.py
index 7e76240a..ebac3d52 100644
--- a/setuptools/_distutils/tests/test_text_file.py
+++ b/setuptools/_distutils/tests/test_text_file.py
@@ -101,7 +101,7 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase):
in_file.close()
def test_suite():
- return unittest.makeSuite(TextFileTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(TextFileTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_unixccompiler.py b/setuptools/_distutils/tests/test_unixccompiler.py
index 63c7dd37..4574f77f 100644
--- a/setuptools/_distutils/tests/test_unixccompiler.py
+++ b/setuptools/_distutils/tests/test_unixccompiler.py
@@ -11,9 +11,12 @@ from distutils.errors import DistutilsPlatformError
from distutils.unixccompiler import UnixCCompiler
from distutils.util import _clear_cached_macosx_ver
-class UnixCCompilerTestCase(unittest.TestCase):
+from . import support
+
+class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
def setUp(self):
+ super().setUp()
self._backup_platform = sys.platform
self._backup_get_config_var = sysconfig.get_config_var
self._backup_get_config_vars = sysconfig.get_config_vars
@@ -23,6 +26,7 @@ class UnixCCompilerTestCase(unittest.TestCase):
self.cc = CompilerWrapper()
def tearDown(self):
+ super().tearDown()
sys.platform = self._backup_platform
sysconfig.get_config_var = self._backup_get_config_var
sysconfig.get_config_vars = self._backup_get_config_vars
@@ -237,11 +241,12 @@ class UnixCCompilerTestCase(unittest.TestCase):
# ensure that setting output_dir does not raise
# FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
self.cc.output_dir = 'scratch'
+ os.chdir(self.mkdtemp())
self.cc.has_function('abort', includes=['stdlib.h'])
def test_suite():
- return unittest.makeSuite(UnixCCompilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(UnixCCompilerTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_upload.py b/setuptools/_distutils/tests/test_upload.py
index bca5516d..ce3e84a2 100644
--- a/setuptools/_distutils/tests/test_upload.py
+++ b/setuptools/_distutils/tests/test_upload.py
@@ -217,7 +217,7 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
def test_suite():
- return unittest.makeSuite(uploadTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(uploadTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_util.py b/setuptools/_distutils/tests/test_util.py
index bf0d4333..2738388e 100644
--- a/setuptools/_distutils/tests/test_util.py
+++ b/setuptools/_distutils/tests/test_util.py
@@ -2,6 +2,7 @@
import os
import sys
import unittest
+import sysconfig as stdlib_sysconfig
from copy import copy
from test.support import run_unittest
from unittest import mock
@@ -10,12 +11,10 @@ from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError
from distutils.util import (get_platform, convert_path, change_root,
check_environ, split_quoted, strtobool,
rfc822_escape, byte_compile,
- grok_environment_error)
+ grok_environment_error, get_host_platform)
from distutils import util # used to patch _environ_checked
-from distutils.sysconfig import get_config_vars
from distutils import sysconfig
from distutils.tests import support
-import _osx_support
class UtilTestCase(support.EnvironGuard, unittest.TestCase):
@@ -63,110 +62,26 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
def _get_uname(self):
return self._uname
- def test_get_platform(self):
-
- # windows XP, 32bits
- os.name = 'nt'
- sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
- '[MSC v.1310 32 bit (Intel)]')
- sys.platform = 'win32'
- self.assertEqual(get_platform(), 'win32')
-
- # windows XP, amd64
- os.name = 'nt'
- sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
- '[MSC v.1310 32 bit (Amd64)]')
- sys.platform = 'win32'
- self.assertEqual(get_platform(), 'win-amd64')
-
- # macbook
- os.name = 'posix'
- sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) '
- '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]')
- sys.platform = 'darwin'
- self._set_uname(('Darwin', 'macziade', '8.11.1',
- ('Darwin Kernel Version 8.11.1: '
- 'Wed Oct 10 18:23:28 PDT 2007; '
- 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386'))
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
-
- get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g '
- '-fwrapv -O3 -Wall -Wstrict-prototypes')
-
- cursize = sys.maxsize
- sys.maxsize = (2 ** 31)-1
- try:
- self.assertEqual(get_platform(), 'macosx-10.3-i386')
- finally:
- sys.maxsize = cursize
-
- # macbook with fat binaries (fat, universal or fat64)
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4'
- get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
-
- self.assertEqual(get_platform(), 'macosx-10.4-fat')
-
- _osx_support._remove_original_values(get_config_vars())
- os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1'
- self.assertEqual(get_platform(), 'macosx-10.4-fat')
-
+ def test_get_host_platform(self):
+ with unittest.mock.patch('os.name', 'nt'):
+ with unittest.mock.patch('sys.version', '... [... (ARM64)]'):
+ self.assertEqual(get_host_platform(), 'win-arm64')
+ with unittest.mock.patch('sys.version', '... [... (ARM)]'):
+ self.assertEqual(get_host_platform(), 'win-arm32')
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
+ with unittest.mock.patch('sys.version_info', (3, 9, 0, 'final', 0)):
+ self.assertEqual(get_host_platform(), stdlib_sysconfig.get_platform())
- self.assertEqual(get_platform(), 'macosx-10.4-intel')
-
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
- self.assertEqual(get_platform(), 'macosx-10.4-fat3')
-
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
- self.assertEqual(get_platform(), 'macosx-10.4-universal')
-
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
-
- self.assertEqual(get_platform(), 'macosx-10.4-fat64')
-
- for arch in ('ppc', 'i386', 'x86_64', 'ppc64'):
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch %s -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3'%(arch,))
-
- self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,))
-
-
- # linux debian sarge
- os.name = 'posix'
- sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) '
- '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]')
- sys.platform = 'linux2'
- self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7',
- '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686'))
-
- self.assertEqual(get_platform(), 'linux-i686')
-
- # XXX more platforms to tests here
+ def test_get_platform(self):
+ with unittest.mock.patch('os.name', 'nt'):
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x86'}):
+ self.assertEqual(get_platform(), 'win32')
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x64'}):
+ self.assertEqual(get_platform(), 'win-amd64')
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm'}):
+ self.assertEqual(get_platform(), 'win-arm32')
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm64'}):
+ self.assertEqual(get_platform(), 'win-arm64')
def test_convert_path(self):
# linux/mac
@@ -303,7 +218,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(UtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(UtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_version.py b/setuptools/_distutils/tests/test_version.py
index 8671cd2f..8405aa3a 100644
--- a/setuptools/_distutils/tests/test_version.py
+++ b/setuptools/_distutils/tests/test_version.py
@@ -1,11 +1,19 @@
"""Tests for distutils.version."""
import unittest
+import distutils
from distutils.version import LooseVersion
from distutils.version import StrictVersion
from test.support import run_unittest
class VersionTestCase(unittest.TestCase):
+ def setUp(self):
+ self.ctx = distutils.version.suppress_known_deprecation()
+ self.ctx.__enter__()
+
+ def tearDown(self):
+ self.ctx.__exit__(None, None, None)
+
def test_prerelease(self):
version = StrictVersion('1.2.3a1')
self.assertEqual(version.version, (1, 2, 3))
@@ -81,7 +89,7 @@ class VersionTestCase(unittest.TestCase):
(v1, v2, res))
def test_suite():
- return unittest.makeSuite(VersionTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(VersionTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/unix_compat.py b/setuptools/_distutils/tests/unix_compat.py
new file mode 100644
index 00000000..b7718c26
--- /dev/null
+++ b/setuptools/_distutils/tests/unix_compat.py
@@ -0,0 +1,16 @@
+import sys
+import unittest
+
+try:
+ import grp
+ import pwd
+except ImportError:
+ grp = pwd = None
+
+
+UNIX_ID_SUPPORT = grp and pwd
+UID_0_SUPPORT = UNIX_ID_SUPPORT and sys.platform != "cygwin"
+
+require_unix_id = unittest.skipUnless(
+ UNIX_ID_SUPPORT, "Requires grp and pwd support")
+require_uid_0 = unittest.skipUnless(UID_0_SUPPORT, "Requires UID 0 support")
diff --git a/setuptools/_distutils/util.py b/setuptools/_distutils/util.py
index afc23c4e..6d506d7e 100644
--- a/setuptools/_distutils/util.py
+++ b/setuptools/_distutils/util.py
@@ -9,6 +9,7 @@ import re
import importlib.util
import string
import sys
+import sysconfig
from distutils.errors import DistutilsPlatformError
from distutils.dep_util import newer
from distutils.spawn import spawn
@@ -20,82 +21,29 @@ from .py35compat import _optim_args_from_interpreter_flags
def get_host_platform():
"""Return a string that identifies the current platform. This is used mainly to
distinguish platform-specific build directories and platform-specific built
- distributions. Typically includes the OS name and version and the
- architecture (as supplied by 'os.uname()'), although the exact information
- included depends on the OS; eg. on Linux, the kernel version isn't
- particularly important.
+ distributions.
+ """
- Examples of returned values:
- linux-i586
- linux-alpha (?)
- solaris-2.6-sun4u
+ # We initially exposed platforms as defined in Python 3.9
+ # even with older Python versions when distutils was split out.
+ # Now that we delegate to stdlib sysconfig we need to restore this
+ # in case anyone has started to depend on it.
- Windows will return one of:
- win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
- win32 (all others - specifically, sys.platform is returned)
+ if sys.version_info < (3, 8):
+ if os.name == 'nt':
+ if '(arm)' in sys.version.lower():
+ return 'win-arm32'
+ if '(arm64)' in sys.version.lower():
+ return 'win-arm64'
- For other non-POSIX platforms, currently just returns 'sys.platform'.
+ 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)
- """
- if os.name == 'nt':
- if 'amd64' in sys.version.lower():
- return 'win-amd64'
- if '(arm)' in sys.version.lower():
- return 'win-arm32'
- if '(arm64)' in sys.version.lower():
- return 'win-arm64'
- return sys.platform
-
- # Set for cross builds explicitly
- if "_PYTHON_HOST_PLATFORM" in os.environ:
- return os.environ["_PYTHON_HOST_PLATFORM"]
-
- if os.name != "posix" or not hasattr(os, 'uname'):
- # XXX what about the architecture? NT is Intel or Alpha,
- # Mac OS is M68k or PPC, etc.
- return sys.platform
-
- # Try to distinguish various flavours of Unix
-
- (osname, host, release, version, machine) = os.uname()
-
- # Convert the OS name to lowercase, remove '/' characters, and translate
- # spaces (for "Power Macintosh")
- osname = osname.lower().replace('/', '')
- machine = machine.replace(' ', '_')
- machine = machine.replace('/', '-')
-
- if osname[:5] == "linux":
- # At least on Linux/Intel, 'machine' is the processor --
- # i386, etc.
- # XXX what about Alpha, SPARC, etc?
- return "%s-%s" % (osname, machine)
- elif osname[:5] == "sunos":
- if release[0] >= "5": # SunOS 5 == Solaris 2
- osname = "solaris"
- release = "%d.%s" % (int(release[0]) - 3, release[2:])
- # We can't use "platform.architecture()[0]" because a
- # bootstrap problem. We use a dict to get an error
- # if some suspicious happens.
- bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
- machine += ".%s" % bitness[sys.maxsize]
- # fall through to standard osname-release-machine representation
- elif osname[:3] == "aix":
- from .py38compat import aix_platform
- return aix_platform(osname, version, release)
- elif osname[:6] == "cygwin":
- osname = "cygwin"
- rel_re = re.compile (r'[\d.]+', re.ASCII)
- m = rel_re.match(release)
- if m:
- release = m.group()
- elif osname[:6] == "darwin":
- import _osx_support, distutils.sysconfig
- osname, release, machine = _osx_support.get_platform_osx(
- distutils.sysconfig.get_config_vars(),
- osname, release, machine)
-
- return "%s-%s-%s" % (osname, release, machine)
+ return sysconfig.get_platform()
def get_platform():
if os.name == 'nt':
@@ -242,30 +190,43 @@ def check_environ ():
def subst_vars (s, local_vars):
- """Perform shell/Perl-style variable substitution on 'string'. Every
- occurrence of '$' followed by a name is considered a variable, and
- variable is substituted by the value found in the 'local_vars'
- dictionary, or in 'os.environ' if it's not in '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()
- def _subst (match, local_vars=local_vars):
- var_name = match.group(1)
- if var_name in local_vars:
- return str(local_vars[var_name])
- else:
- return os.environ[var_name]
-
+ lookup = dict(os.environ)
+ lookup.update((name, str(value)) for name, value in local_vars.items())
try:
- return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
+ return _subst_compat(s).format_map(lookup)
except KeyError as var:
- raise ValueError("invalid variable '$%s'" % var)
+ raise ValueError(f"invalid variable {var}")
# subst_vars ()
+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 substitions are deprecated",
+ DeprecationWarning,
+ )
+ return repl
+
+
def grok_environment_error (exc, prefix="error: "):
# Function kept for backward compatibility.
# Used to try clever things with EnvironmentErrors,
diff --git a/setuptools/_distutils/version.py b/setuptools/_distutils/version.py
index c33bebae..35e181db 100644
--- a/setuptools/_distutils/version.py
+++ b/setuptools/_distutils/version.py
@@ -27,6 +27,20 @@ Every version number class implements the following interface:
"""
import re
+import warnings
+import contextlib
+
+
+@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
@@ -36,6 +50,12 @@ class Version:
"""
def __init__ (self, vstring=None):
+ warnings.warn(
+ "distutils Version classes are deprecated. "
+ "Use packaging.version instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
if vstring:
self.parse(vstring)
@@ -165,7 +185,8 @@ class StrictVersion (Version):
def _cmp (self, other):
if isinstance(other, str):
- other = StrictVersion(other)
+ with suppress_known_deprecation():
+ other = StrictVersion(other)
elif not isinstance(other, StrictVersion):
return NotImplemented
@@ -301,11 +322,6 @@ class LooseVersion (Version):
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
- def __init__ (self, vstring=None):
- if vstring:
- self.parse(vstring)
-
-
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
diff --git a/setuptools/_distutils/versionpredicate.py b/setuptools/_distutils/versionpredicate.py
index 062c98f2..55f25d91 100644
--- a/setuptools/_distutils/versionpredicate.py
+++ b/setuptools/_distutils/versionpredicate.py
@@ -23,7 +23,9 @@ def splitUp(pred):
if not res:
raise ValueError("bad package restriction syntax: %r" % pred)
comp, verStr = res.groups()
- return (comp, distutils.version.StrictVersion(verStr))
+ with distutils.version.suppress_known_deprecation():
+ other = distutils.version.StrictVersion(verStr)
+ return (comp, other)
compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq,
">": operator.gt, ">=": operator.ge, "!=": operator.ne}
@@ -162,5 +164,6 @@ def split_provision(value):
raise ValueError("illegal provides specification: %r" % value)
ver = m.group(2) or None
if ver:
- ver = distutils.version.StrictVersion(ver)
+ with distutils.version.suppress_known_deprecation():
+ ver = distutils.version.StrictVersion(ver)
return m.group(1), ver
diff --git a/setuptools/_importlib.py b/setuptools/_importlib.py
new file mode 100644
index 00000000..c529ccd3
--- /dev/null
+++ b/setuptools/_importlib.py
@@ -0,0 +1,13 @@
+import sys
+
+
+if sys.version_info < (3, 10):
+ from setuptools.extern import importlib_metadata as 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/setuptools/_path.py b/setuptools/_path.py
new file mode 100644
index 00000000..ede9cb00
--- /dev/null
+++ b/setuptools/_path.py
@@ -0,0 +1,7 @@
+import os
+
+
+def ensure_directory(path):
+ """Ensure that the parent directory of `path` exists"""
+ dirname = os.path.dirname(path)
+ os.makedirs(dirname, exist_ok=True)
diff --git a/setuptools/_reqs.py b/setuptools/_reqs.py
new file mode 100644
index 00000000..ca724174
--- /dev/null
+++ b/setuptools/_reqs.py
@@ -0,0 +1,19 @@
+import setuptools.extern.jaraco.text as text
+
+from pkg_resources import Requirement
+
+
+def parse_strings(strs):
+ """
+ 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)))
+
+
+def parse(strs):
+ """
+ Deprecated drop-in replacement for pkg_resources.parse_requirements.
+ """
+ return map(Requirement, parse_strings(strs))
diff --git a/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/INSTALLER b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/LICENSE b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/LICENSE
new file mode 100644
index 00000000..be7e092b
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2017-2019 Jason R. Coombs, Barry Warsaw
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/METADATA b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/METADATA
new file mode 100644
index 00000000..7327b888
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/METADATA
@@ -0,0 +1,118 @@
+Metadata-Version: 2.1
+Name: importlib-metadata
+Version: 4.10.1
+Summary: Read metadata from Python packages
+Home-page: https://github.com/python/importlib_metadata
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: zipp (>=0.5)
+Requires-Dist: typing-extensions (>=3.6.4) ; python_version < "3.8"
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: perf
+Requires-Dist: ipython ; extra == 'perf'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: packaging ; extra == 'testing'
+Requires-Dist: pyfakefs ; extra == 'testing'
+Requires-Dist: flufl.flake8 ; extra == 'testing'
+Requires-Dist: pytest-perf (>=0.9.2) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: importlib-resources (>=1.3) ; (python_version < "3.9") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/importlib_metadata.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/importlib_metadata.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/importlib_metadata
+
+.. image:: https://github.com/python/importlib_metadata/workflows/tests/badge.svg
+ :target: https://github.com/python/importlib_metadata/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/importlib-metadata/badge/?version=latest
+ :target: https://importlib-metadata.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+
+Library to access the metadata for a Python package.
+
+This package supplies third-party access to the functionality of
+`importlib.metadata <https://docs.python.org/3/library/importlib.metadata.html>`_
+including improvements added to subsequent Python versions.
+
+
+Compatibility
+=============
+
+New features are introduced in this third-party library and later merged
+into CPython. The following table indicates which versions of this library
+were contributed to different versions in the standard library:
+
+.. list-table::
+ :header-rows: 1
+
+ * - importlib_metadata
+ - stdlib
+ * - 4.8
+ - 3.11
+ * - 4.4
+ - 3.10
+ * - 1.4
+ - 3.8
+
+
+Usage
+=====
+
+See the `online documentation <https://importlib_metadata.readthedocs.io/>`_
+for usage details.
+
+`Finder authors
+<https://docs.python.org/3/reference/import.html#finders-and-loaders>`_ can
+also add support for custom package installers. See the above documentation
+for details.
+
+
+Caveats
+=======
+
+This project primarily supports third-party packages installed by PyPA
+tools (or other conforming packages). It does not support:
+
+- Packages in the stdlib.
+- Packages installed without metadata.
+
+Project details
+===============
+
+ * Project home: https://github.com/python/importlib_metadata
+ * Report bugs at: https://github.com/python/importlib_metadata/issues
+ * Code hosting: https://github.com/python/importlib_metadata
+ * Documentation: https://importlib_metadata.readthedocs.io/
+
+
diff --git a/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/RECORD b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/RECORD
new file mode 100644
index 00000000..ebedf904
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/RECORD
@@ -0,0 +1,24 @@
+importlib_metadata-4.10.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+importlib_metadata-4.10.1.dist-info/LICENSE,sha256=wNe6dAchmJ1VvVB8D9oTc-gHHadCuaSBAev36sYEM6U,571
+importlib_metadata-4.10.1.dist-info/METADATA,sha256=-HDYj3iK6bcjwN5MAoO58Op6WQIYQfbhl6ZaPqL0IZI,3989
+importlib_metadata-4.10.1.dist-info/RECORD,,
+importlib_metadata-4.10.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_metadata-4.10.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+importlib_metadata-4.10.1.dist-info/top_level.txt,sha256=CO3fD9yylANiXkrMo4qHLV_mqXL2sC5JFKgt1yWAT-A,19
+importlib_metadata/__init__.py,sha256=7WxDdbPPu4Wy3VeMTApd-JlPQoENgVDyDH6aqyE7acE,30175
+importlib_metadata/__pycache__/__init__.cpython-310.pyc,,
+importlib_metadata/__pycache__/_adapters.cpython-310.pyc,,
+importlib_metadata/__pycache__/_collections.cpython-310.pyc,,
+importlib_metadata/__pycache__/_compat.cpython-310.pyc,,
+importlib_metadata/__pycache__/_functools.cpython-310.pyc,,
+importlib_metadata/__pycache__/_itertools.cpython-310.pyc,,
+importlib_metadata/__pycache__/_meta.cpython-310.pyc,,
+importlib_metadata/__pycache__/_text.cpython-310.pyc,,
+importlib_metadata/_adapters.py,sha256=B6fCi5-8mLVDFUZj3krI5nAo-mKp1dH_qIavyIyFrJs,1862
+importlib_metadata/_collections.py,sha256=CJ0OTCHIjWA0ZIVS4voORAsn2R4R2cQBEtPsZEJpASY,743
+importlib_metadata/_compat.py,sha256=EU2XCFBPFByuI0Of6XkAuBYbzqSyjwwwwqmsK4ccna0,1826
+importlib_metadata/_functools.py,sha256=PsY2-4rrKX4RVeRC1oGp1lB1pmC9eKN88_f-bD9uOoA,2895
+importlib_metadata/_itertools.py,sha256=cvr_2v8BRbxcIl5x5ldfqdHjhI8Yi8s8yk50G_nm6jQ,2068
+importlib_metadata/_meta.py,sha256=_F48Hu_jFxkfKWz5wcYS8vO23qEygbVdF9r-6qh-hjE,1154
+importlib_metadata/_text.py,sha256=HCsFksZpJLeTP3NEk_ngrAeXVRRtTrtyh9eOABoRP4A,2166
+importlib_metadata/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/REQUESTED b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/REQUESTED
diff --git a/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/WHEEL b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/WHEEL
new file mode 100644
index 00000000..becc9a66
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/top_level.txt b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/top_level.txt
new file mode 100644
index 00000000..bbb07547
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata-4.10.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+importlib_metadata
diff --git a/setuptools/_vendor/importlib_metadata/__init__.py b/setuptools/_vendor/importlib_metadata/__init__.py
new file mode 100644
index 00000000..7713e1e0
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata/__init__.py
@@ -0,0 +1,1051 @@
+import os
+import re
+import abc
+import csv
+import sys
+import zipp
+import email
+import pathlib
+import operator
+import textwrap
+import warnings
+import functools
+import itertools
+import posixpath
+import collections
+
+from . import _adapters, _meta
+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, Union
+
+
+__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
+ """
+
+ _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
+ <https://packaging.python.org/specifications/entry-points/>`_
+ for more information.
+ """
+
+ pattern = re.compile(
+ r'(?P<module>[\w.]+)\s*'
+ r'(:\s*(?P<attr>[\w.]+)\s*)?'
+ r'((?P<extras>\[.*\])\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.
+ """
+
+ 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 list(re.finditer(r'\w+', match.group('extras') or ''))
+
+ def _for(self, dist):
+ vars(self).update(dist=dist)
+ return self
+
+ def __iter__(self):
+ """
+ Supply iter so one may construct dicts of EntryPoints by name.
+ """
+ msg = (
+ "Construction of dict of EntryPoints is deprecated in "
+ "favor of EntryPoints."
+ )
+ warnings.warn(msg, DeprecationWarning)
+ return iter((self.name, self))
+
+ def matches(self, **params):
+ 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 DeprecatedList(list):
+ """
+ Allow an otherwise immutable object to implement mutability
+ for compatibility.
+
+ >>> recwarn = getfixture('recwarn')
+ >>> dl = DeprecatedList(range(3))
+ >>> dl[0] = 1
+ >>> dl.append(3)
+ >>> del dl[3]
+ >>> dl.reverse()
+ >>> dl.sort()
+ >>> dl.extend([4])
+ >>> dl.pop(-1)
+ 4
+ >>> dl.remove(1)
+ >>> dl += [5]
+ >>> dl + [6]
+ [1, 2, 5, 6]
+ >>> dl + (6,)
+ [1, 2, 5, 6]
+ >>> dl.insert(0, 0)
+ >>> dl
+ [0, 1, 2, 5]
+ >>> dl == [0, 1, 2, 5]
+ True
+ >>> dl == (0, 1, 2, 5)
+ True
+ >>> len(recwarn)
+ 1
+ """
+
+ _warn = functools.partial(
+ warnings.warn,
+ "EntryPoints list interface is deprecated. Cast to list if needed.",
+ DeprecationWarning,
+ stacklevel=pypy_partial(2),
+ )
+
+ def _wrap_deprecated_method(method_name: str): # type: ignore
+ def wrapped(self, *args, **kwargs):
+ self._warn()
+ return getattr(super(), method_name)(*args, **kwargs)
+
+ return wrapped
+
+ for method_name in [
+ '__setitem__',
+ '__delitem__',
+ 'append',
+ 'reverse',
+ 'extend',
+ 'pop',
+ 'remove',
+ '__iadd__',
+ 'insert',
+ 'sort',
+ ]:
+ locals()[method_name] = _wrap_deprecated_method(method_name)
+
+ def __add__(self, other):
+ if not isinstance(other, tuple):
+ self._warn()
+ other = tuple(other)
+ return self.__class__(tuple(self) + other)
+
+ def __eq__(self, other):
+ if not isinstance(other, tuple):
+ self._warn()
+ other = tuple(other)
+
+ return tuple(self).__eq__(other)
+
+
+class EntryPoints(DeprecatedList):
+ """
+ An immutable collection of selectable EntryPoint objects.
+ """
+
+ __slots__ = ()
+
+ def __getitem__(self, name): # -> EntryPoint:
+ """
+ Get the EntryPoint in self matching name.
+ """
+ if isinstance(name, int):
+ warnings.warn(
+ "Accessing entry points by index is deprecated. "
+ "Cast to tuple if needed.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return super().__getitem__(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 ep.matches(**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.
+
+ For coverage while SelectableGroups is present.
+ >>> EntryPoints().groups
+ set()
+ """
+ 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 Deprecated:
+ """
+ Compatibility add-in for mapping to indicate that
+ mapping behavior is deprecated.
+
+ >>> recwarn = getfixture('recwarn')
+ >>> class DeprecatedDict(Deprecated, dict): pass
+ >>> dd = DeprecatedDict(foo='bar')
+ >>> dd.get('baz', None)
+ >>> dd['foo']
+ 'bar'
+ >>> list(dd)
+ ['foo']
+ >>> list(dd.keys())
+ ['foo']
+ >>> 'foo' in dd
+ True
+ >>> list(dd.values())
+ ['bar']
+ >>> len(recwarn)
+ 1
+ """
+
+ _warn = functools.partial(
+ warnings.warn,
+ "SelectableGroups dict interface is deprecated. Use select.",
+ DeprecationWarning,
+ stacklevel=pypy_partial(2),
+ )
+
+ def __getitem__(self, name):
+ self._warn()
+ return super().__getitem__(name)
+
+ def get(self, name, default=None):
+ self._warn()
+ return super().get(name, default)
+
+ def __iter__(self):
+ self._warn()
+ return super().__iter__()
+
+ def __contains__(self, *args):
+ self._warn()
+ return super().__contains__(*args)
+
+ def keys(self):
+ self._warn()
+ return super().keys()
+
+ def values(self):
+ self._warn()
+ return super().values()
+
+
+class SelectableGroups(Deprecated, dict):
+ """
+ A backward- and forward-compatible result from
+ entry_points that fully implements the dict interface.
+ """
+
+ @classmethod
+ def load(cls, eps):
+ by_group = operator.attrgetter('group')
+ ordered = sorted(eps, key=by_group)
+ grouped = itertools.groupby(ordered, by_group)
+ return cls((group, EntryPoints(eps)) for group, eps in grouped)
+
+ @property
+ def _all(self):
+ """
+ Reconstruct a list of all entrypoints from the groups.
+ """
+ groups = super(Deprecated, self).values()
+ return EntryPoints(itertools.chain.from_iterable(groups))
+
+ @property
+ def groups(self):
+ return self._all.groups
+
+ @property
+ def names(self):
+ """
+ for coverage:
+ >>> SelectableGroups().names
+ set()
+ """
+ return self._all.names
+
+ def select(self, **params):
+ if not params:
+ return self
+ return self._all.select(**params)
+
+
+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'<FileHash mode: {self.mode} value: {self.value}>'
+
+
+class Distribution:
+ """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):
+ """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.
+ """
+ for resolver in cls._discover_resolvers():
+ dists = resolver(DistributionFinder.Context(name=name))
+ dist = next(iter(dists), None)
+ if dist is not None:
+ return dist
+ else:
+ 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 source and 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 = str(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 self._name_from_stem(stem) or super()._normalized_name
+
+ def _name_from_stem(self, stem):
+ name, ext = os.path.splitext(stem)
+ if ext not in ('.dist-info', '.egg-info'):
+ return
+ name, sep, rest = stem.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
+
+
+def entry_points(**params) -> Union[EntryPoints, SelectableGroups]:
+ """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()).
+
+ For compatibility, returns ``SelectableGroups`` object unless
+ selection parameters are supplied. In the future, this function
+ will return ``EntryPoints`` instead of ``SelectableGroups``
+ even when no selection parameters are supplied.
+
+ For maximum future compatibility, pass selection parameters
+ or invoke ``.select`` with parameters on the result.
+
+ :return: EntryPoints or SelectableGroups for all installed packages.
+ """
+ norm_name = operator.attrgetter('_normalized_name')
+ unique = functools.partial(unique_everseen, key=norm_name)
+ eps = itertools.chain.from_iterable(
+ dist.entry_points for dist in unique(distributions())
+ )
+ return SelectableGroups.load(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/setuptools/_vendor/importlib_metadata/_adapters.py b/setuptools/_vendor/importlib_metadata/_adapters.py
new file mode 100644
index 00000000..aa460d3e
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata/_adapters.py
@@ -0,0 +1,68 @@
+import re
+import textwrap
+import email.message
+
+from ._text import FoldedCase
+
+
+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 _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/setuptools/_vendor/importlib_metadata/_collections.py b/setuptools/_vendor/importlib_metadata/_collections.py
new file mode 100644
index 00000000..cf0954e1
--- /dev/null
+++ b/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/setuptools/_vendor/importlib_metadata/_compat.py b/setuptools/_vendor/importlib_metadata/_compat.py
new file mode 100644
index 00000000..8fe4e4e3
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata/_compat.py
@@ -0,0 +1,71 @@
+import sys
+import platform
+
+
+__all__ = ['install', 'NullFinder', 'Protocol']
+
+
+try:
+ from typing import Protocol
+except ImportError: # pragma: no cover
+ 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/setuptools/_vendor/importlib_metadata/_functools.py b/setuptools/_vendor/importlib_metadata/_functools.py
new file mode 100644
index 00000000..71f66bd0
--- /dev/null
+++ b/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/setuptools/_vendor/importlib_metadata/_itertools.py b/setuptools/_vendor/importlib_metadata/_itertools.py
new file mode 100644
index 00000000..d4ca9b91
--- /dev/null
+++ b/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/setuptools/_vendor/importlib_metadata/_meta.py b/setuptools/_vendor/importlib_metadata/_meta.py
new file mode 100644
index 00000000..37ee43e6
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata/_meta.py
@@ -0,0 +1,48 @@
+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):
+ """
+ A minimal subset of pathlib.Path required by PathDistribution.
+ """
+
+ def joinpath(self) -> 'SimplePath':
+ ... # pragma: no cover
+
+ def __truediv__(self) -> 'SimplePath':
+ ... # pragma: no cover
+
+ def parent(self) -> 'SimplePath':
+ ... # pragma: no cover
+
+ def read_text(self) -> str:
+ ... # pragma: no cover
diff --git a/setuptools/_vendor/importlib_metadata/_text.py b/setuptools/_vendor/importlib_metadata/_text.py
new file mode 100644
index 00000000..c88cfbb2
--- /dev/null
+++ b/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/setuptools/_vendor/importlib_metadata/py.typed b/setuptools/_vendor/importlib_metadata/py.typed
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_metadata/py.typed
diff --git a/setuptools/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/importlib_resources-5.4.0.dist-info/LICENSE b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/LICENSE
new file mode 100644
index 00000000..378b991a
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2017-2019 Brett Cannon, Barry Warsaw
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/setuptools/_vendor/importlib_resources-5.4.0.dist-info/METADATA b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/METADATA
new file mode 100644
index 00000000..cdb1e783
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/METADATA
@@ -0,0 +1,86 @@
+Metadata-Version: 2.1
+Name: importlib-resources
+Version: 5.4.0
+Summary: Read resources from Python packages
+Home-page: https://github.com/python/importlib_resources
+Author: Barry Warsaw
+Author-email: barry@python.org
+License: UNKNOWN
+Project-URL: Documentation, https://importlib-resources.readthedocs.io/
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.6
+License-File: LICENSE
+Requires-Dist: zipp (>=3.1.0) ; python_version < "3.10"
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/importlib_resources.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/importlib_resources.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/importlib_resources
+
+.. image:: https://github.com/python/importlib_resources/workflows/tests/badge.svg
+ :target: https://github.com/python/importlib_resources/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/importlib-resources/badge/?version=latest
+ :target: https://importlib-resources.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+``importlib_resources`` is a backport of Python standard library
+`importlib.resources
+<https://docs.python.org/3/library/importlib.html#module-importlib.resources>`_
+module for older Pythons.
+
+The key goal of this module is to replace parts of `pkg_resources
+<https://setuptools.readthedocs.io/en/latest/pkg_resources.html>`_ with a
+solution in Python's stdlib that relies on well-defined APIs. This makes
+reading resources included in packages easier, with more stable and consistent
+semantics.
+
+Compatibility
+=============
+
+New features are introduced in this third-party library and later merged
+into CPython. The following table indicates which versions of this library
+were contributed to different versions in the standard library:
+
+.. list-table::
+ :header-rows: 1
+
+ * - importlib_resources
+ - stdlib
+ * - 5.2
+ - 3.11
+ * - 5.0
+ - 3.10
+ * - 1.3
+ - 3.9
+ * - 0.5 (?)
+ - 3.7
+
+
diff --git a/setuptools/_vendor/importlib_resources-5.4.0.dist-info/RECORD b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/RECORD
new file mode 100644
index 00000000..7a68a2f2
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/RECORD
@@ -0,0 +1,75 @@
+importlib_resources-5.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+importlib_resources-5.4.0.dist-info/LICENSE,sha256=uWRjFdYGataJX2ziXk048ItUglQmjng3GWBALaWA36U,568
+importlib_resources-5.4.0.dist-info/METADATA,sha256=i5jH25IbM0Ls6u6UzSSCOa0c8hpDvePxqgnQwh2T5Io,3135
+importlib_resources-5.4.0.dist-info/RECORD,,
+importlib_resources-5.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources-5.4.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+importlib_resources-5.4.0.dist-info/top_level.txt,sha256=fHIjHU1GZwAjvcydpmUnUrTnbvdiWjG4OEVZK8by0TQ,20
+importlib_resources/__init__.py,sha256=zuA0lbRgtVVCcAztM0z5LuBiOCV9L_3qtI6mW2p5xAg,525
+importlib_resources/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/__pycache__/_adapters.cpython-310.pyc,,
+importlib_resources/__pycache__/_common.cpython-310.pyc,,
+importlib_resources/__pycache__/_compat.cpython-310.pyc,,
+importlib_resources/__pycache__/_itertools.cpython-310.pyc,,
+importlib_resources/__pycache__/_legacy.cpython-310.pyc,,
+importlib_resources/__pycache__/abc.cpython-310.pyc,,
+importlib_resources/__pycache__/readers.cpython-310.pyc,,
+importlib_resources/__pycache__/simple.cpython-310.pyc,,
+importlib_resources/_adapters.py,sha256=o51tP2hpVtohP33gSYyAkGNpLfYDBqxxYsadyiRZi1E,4504
+importlib_resources/_common.py,sha256=iIxAaQhotSh6TLLUEfL_ynU2fzEeyHMz9JcL46mUhLg,2741
+importlib_resources/_compat.py,sha256=3LpkIfeN9x4oXjRea5TxZP5VYhPlzuVRhGe-hEv-S0s,2704
+importlib_resources/_itertools.py,sha256=WCdJ1Gs_kNFwKENyIG7TO0Y434IWCu0zjVVSsSbZwU8,884
+importlib_resources/_legacy.py,sha256=TMLkx6aEM6U8xIREPXqGZrMbUhTiPUuPl6ESD7RdYj4,3494
+importlib_resources/abc.py,sha256=MvTJJXajbl74s36Gyeesf76egtbFnh-TMtzQMVhFWXo,3886
+importlib_resources/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/readers.py,sha256=_9QLGQ5AzrED3PY8S2Zf8V6yLR0-nqqYqtQmgleDJzY,3566
+importlib_resources/simple.py,sha256=xt0qhXbwt3bZ86zuaaKbTiE9A0mDbwu0saRjUq_pcY0,2836
+importlib_resources/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/_compat.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_compatibilty_files.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_contents.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_files.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_open.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_path.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_read.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_reader.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/test_resource.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/update-zips.cpython-310.pyc,,
+importlib_resources/tests/__pycache__/util.cpython-310.pyc,,
+importlib_resources/tests/_compat.py,sha256=QGI_4p0DXybypoYvw0kr3jfQqvls3p8u4wy4Wvf0Z_o,435
+importlib_resources/tests/data01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/subdirectory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data01/subdirectory/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data01/subdirectory/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/data01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/data01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/data02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data02/one/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/one/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data02/one/resource1.txt,sha256=10flKac7c-XXFzJ3t-AB5MJjlBy__dSZvPE_dOm2q6U,13
+importlib_resources/tests/data02/two/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/data02/two/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/data02/two/resource2.txt,sha256=lt2jbN3TMn9QiFKM832X39bU_62UptDdUkoYzkvEbl0,13
+importlib_resources/tests/namespacedata01/binary.file,sha256=BU7ewdAhH2JP7Qy8qdT5QAsOSRxDdCryxbCr6_DJkNg,4
+importlib_resources/tests/namespacedata01/utf-16.file,sha256=t5q9qhxX0rYqItBOM8D3ylwG-RHrnOYteTLtQr6sF7g,44
+importlib_resources/tests/namespacedata01/utf-8.file,sha256=kwWgYG4yQ-ZF2X_WA66EjYPmxJRn-w8aSOiS9e8tKYY,20
+importlib_resources/tests/test_compatibilty_files.py,sha256=NWkbIsylI8Wz3Dwsxo1quT4ZI6ToXFA2mojCG6Dzuxw,3260
+importlib_resources/tests/test_contents.py,sha256=V1Xfk3lqTDdvUsZuV18Kndf0CT_tkM2oEIwk9Vv0rhg,968
+importlib_resources/tests/test_files.py,sha256=1Nqv6VM_MjfwrmtXYL1a1CMT0QhCxi3hNMqwXlfMQTg,1184
+importlib_resources/tests/test_open.py,sha256=pmEgdrSFdM83L6FxtR8U_RT9BfI3JZ4snGmM_ZZIegY,2565
+importlib_resources/tests/test_path.py,sha256=xvPteNA-UKavDhKgLgrQuXSxKWYH7Q4nSNDVfBX95Gs,2103
+importlib_resources/tests/test_read.py,sha256=EyYvpHJ_7F4LuX2EU_c5EerIBQfRhOFmiIR7LOc5Y5E,2408
+importlib_resources/tests/test_reader.py,sha256=hgXHquqAEnioemv20ZZcDlVaiOrcZKADO37_FkiQ00Y,4286
+importlib_resources/tests/test_resource.py,sha256=DqfLNc9kaN5obqxU8kn0sRUWMf9MygagrpfMV5-QfWg,8145
+importlib_resources/tests/update-zips.py,sha256=x3iJVqWnMM5qp4Oob2Pl3o6Yi03sUjEv_5Wf-UCg3ps,1415
+importlib_resources/tests/util.py,sha256=X1j-0C96pu3_tmtJuLhzfBfcfMenOphDLkxtCt5j7t4,5309
+importlib_resources/tests/zipdata01/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata01/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/zipdata01/ziptestdata.zip,sha256=z5Of4dsv3T0t-46B0MsVhxlhsPGMz28aUhJDWpj3_oY,876
+importlib_resources/tests/zipdata02/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+importlib_resources/tests/zipdata02/__pycache__/__init__.cpython-310.pyc,,
+importlib_resources/tests/zipdata02/ziptestdata.zip,sha256=ydI-_j-xgQ7tDxqBp9cjOqXBGxUp6ZBbwVJu6Xj-nrY,698
diff --git a/setuptools/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/REQUESTED
diff --git a/setuptools/_vendor/importlib_resources-5.4.0.dist-info/WHEEL b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt
new file mode 100644
index 00000000..58ad1bd3
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources-5.4.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+importlib_resources
diff --git a/setuptools/_vendor/importlib_resources/__init__.py b/setuptools/_vendor/importlib_resources/__init__.py
new file mode 100644
index 00000000..34e3a995
--- /dev/null
+++ b/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/setuptools/_vendor/importlib_resources/_adapters.py b/setuptools/_vendor/importlib_resources/_adapters.py
new file mode 100644
index 00000000..ea363d86
--- /dev/null
+++ b/setuptools/_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/setuptools/_vendor/importlib_resources/_common.py b/setuptools/_vendor/importlib_resources/_common.py
new file mode 100644
index 00000000..a12e2c75
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/_common.py
@@ -0,0 +1,104 @@
+import os
+import pathlib
+import tempfile
+import functools
+import contextlib
+import types
+import importlib
+
+from typing import Union, Optional
+from .abc import ResourceReader, Traversable
+
+from ._compat import wrap_spec
+
+Package = Union[types.ModuleType, str]
+
+
+def files(package):
+ # type: (Package) -> Traversable
+ """
+ Get a Traversable resource from a package
+ """
+ return from_package(get_package(package))
+
+
+def get_resource_reader(package):
+ # type: (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
+
+
+def resolve(cand):
+ # type: (Package) -> types.ModuleType
+ return cand if isinstance(cand, types.ModuleType) else importlib.import_module(cand)
+
+
+def get_package(package):
+ # type: (Package) -> types.ModuleType
+ """Take a package name or module object and return the module.
+
+ Raise an exception if the resolved module is not a package.
+ """
+ resolved = resolve(package)
+ if wrap_spec(resolved).submodule_search_locations is None:
+ raise TypeError(f'{package!r} is not a package')
+ return resolved
+
+
+def from_package(package):
+ """
+ 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=''):
+ # 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
+
+
+@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 _tempfile(path.read_bytes, suffix=path.name)
+
+
+@as_file.register(pathlib.Path)
+@contextlib.contextmanager
+def _(path):
+ """
+ Degenerate behavior for pathlib.Path objects.
+ """
+ yield path
diff --git a/setuptools/_vendor/importlib_resources/_compat.py b/setuptools/_vendor/importlib_resources/_compat.py
new file mode 100644
index 00000000..cb9fc820
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/_compat.py
@@ -0,0 +1,98 @@
+# flake8: noqa
+
+import abc
+import sys
+import pathlib
+from contextlib import suppress
+
+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)
diff --git a/setuptools/_vendor/importlib_resources/_itertools.py b/setuptools/_vendor/importlib_resources/_itertools.py
new file mode 100644
index 00000000..cce05582
--- /dev/null
+++ b/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/setuptools/_vendor/importlib_resources/_legacy.py b/setuptools/_vendor/importlib_resources/_legacy.py
new file mode 100644
index 00000000..1d5d3f1f
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/_legacy.py
@@ -0,0 +1,121 @@
+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):
+ # type: (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/setuptools/_vendor/importlib_resources/abc.py b/setuptools/_vendor/importlib_resources/abc.py
new file mode 100644
index 00000000..d39dc1ad
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/abc.py
@@ -0,0 +1,137 @@
+import abc
+from typing import BinaryIO, Iterable, Text
+
+from ._compat import runtime_checkable, Protocol
+
+
+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
+
+
+@runtime_checkable
+class Traversable(Protocol):
+ """
+ An object with a subset of pathlib.Path methods suitable for
+ traversing directories and opening files.
+ """
+
+ @abc.abstractmethod
+ def iterdir(self):
+ """
+ Yield Traversable objects in self
+ """
+
+ def read_bytes(self):
+ """
+ Read contents of self as bytes
+ """
+ with self.open('rb') as strm:
+ return strm.read()
+
+ def read_text(self, encoding=None):
+ """
+ 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
+ """
+
+ @abc.abstractmethod
+ def joinpath(self, child):
+ """
+ Return Traversable child in self
+ """
+
+ def __truediv__(self, child):
+ """
+ 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.
+ """
+
+ @abc.abstractproperty
+ 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):
+ """Return a Traversable object for the loaded package."""
+
+ def open_resource(self, resource):
+ return self.files().joinpath(resource).open('rb')
+
+ def resource_path(self, resource):
+ raise FileNotFoundError(resource)
+
+ def is_resource(self, path):
+ return self.files().joinpath(path).is_file()
+
+ def contents(self):
+ return (item.name for item in self.files().iterdir())
diff --git a/setuptools/_vendor/importlib_resources/py.typed b/setuptools/_vendor/importlib_resources/py.typed
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/py.typed
diff --git a/setuptools/_vendor/importlib_resources/readers.py b/setuptools/_vendor/importlib_resources/readers.py
new file mode 100644
index 00000000..f1190ca4
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/readers.py
@@ -0,0 +1,122 @@
+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, child):
+ # first try to find child in current paths
+ for file in self.iterdir():
+ if file.name == child:
+ return file
+ # if it does not exist, construct it with the first path
+ return self._paths[0] / child
+
+ __truediv__ = joinpath
+
+ 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/setuptools/_vendor/importlib_resources/simple.py b/setuptools/_vendor/importlib_resources/simple.py
new file mode 100644
index 00000000..da073cbd
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/simple.py
@@ -0,0 +1,116 @@
+"""
+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.
+ """
+
+ @abc.abstractproperty
+ def package(self):
+ # type: () -> str
+ """
+ The name of the package for which this reader loads resources.
+ """
+
+ @abc.abstractmethod
+ def children(self):
+ # type: () -> List['SimpleReader']
+ """
+ Obtain an iterable of SimpleReader for available
+ child containers (e.g. directories).
+ """
+
+ @abc.abstractmethod
+ def resources(self):
+ # type: () -> List[str]
+ """
+ Obtain available named resources for this virtual package.
+ """
+
+ @abc.abstractmethod
+ def open_binary(self, resource):
+ # type: (str) -> BinaryIO
+ """
+ Obtain a File-like for a named resource.
+ """
+
+ @property
+ def name(self):
+ return self.package.split('.')[-1]
+
+
+class ResourceHandle(Traversable):
+ """
+ Handle to a named resource in a ResourceReader.
+ """
+
+ def __init__(self, parent, name):
+ # type: (ResourceContainer, str) -> None
+ 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 ResourceContainer(Traversable):
+ """
+ Traversable container for a package's resources via its reader.
+ """
+
+ def __init__(self, reader):
+ # type: (SimpleReader) -> None
+ 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()
+
+ def joinpath(self, name):
+ return next(
+ traversable for traversable in self.iterdir() if traversable.name == name
+ )
+
+
+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/setuptools/_vendor/importlib_resources/tests/__init__.py b/setuptools/_vendor/importlib_resources/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/_compat.py b/setuptools/_vendor/importlib_resources/tests/_compat.py
new file mode 100644
index 00000000..4c99cffd
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/_compat.py
@@ -0,0 +1,19 @@
+import os
+
+
+try:
+ from test.support import import_helper # type: ignore
+except ImportError:
+ # Python 3.9 and earlier
+ class import_helper: # type: ignore
+ from test.support import modules_setup, modules_cleanup
+
+
+try:
+ # Python 3.10
+ from test.support.os_helper import unlink
+except ImportError:
+ from test.support import unlink as _unlink
+
+ def unlink(target):
+ return _unlink(os.fspath(target))
diff --git a/setuptools/_vendor/importlib_resources/tests/data01/__init__.py b/setuptools/_vendor/importlib_resources/tests/data01/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data01/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/data01/binary.file b/setuptools/_vendor/importlib_resources/tests/data01/binary.file
new file mode 100644
index 00000000..eaf36c1d
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data01/binary.file
Binary files differ
diff --git a/setuptools/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py b/setuptools/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data01/subdirectory/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/data01/subdirectory/binary.file b/setuptools/_vendor/importlib_resources/tests/data01/subdirectory/binary.file
new file mode 100644
index 00000000..eaf36c1d
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data01/subdirectory/binary.file
Binary files differ
diff --git a/setuptools/_vendor/importlib_resources/tests/data01/utf-16.file b/setuptools/_vendor/importlib_resources/tests/data01/utf-16.file
new file mode 100644
index 00000000..2cb77229
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data01/utf-16.file
Binary files differ
diff --git a/setuptools/_vendor/importlib_resources/tests/data01/utf-8.file b/setuptools/_vendor/importlib_resources/tests/data01/utf-8.file
new file mode 100644
index 00000000..1c0132ad
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data01/utf-8.file
@@ -0,0 +1 @@
+Hello, UTF-8 world!
diff --git a/setuptools/_vendor/importlib_resources/tests/data02/__init__.py b/setuptools/_vendor/importlib_resources/tests/data02/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data02/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/data02/one/__init__.py b/setuptools/_vendor/importlib_resources/tests/data02/one/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data02/one/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/data02/one/resource1.txt b/setuptools/_vendor/importlib_resources/tests/data02/one/resource1.txt
new file mode 100644
index 00000000..61a813e4
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data02/one/resource1.txt
@@ -0,0 +1 @@
+one resource
diff --git a/setuptools/_vendor/importlib_resources/tests/data02/two/__init__.py b/setuptools/_vendor/importlib_resources/tests/data02/two/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data02/two/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/data02/two/resource2.txt b/setuptools/_vendor/importlib_resources/tests/data02/two/resource2.txt
new file mode 100644
index 00000000..a80ce46e
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/data02/two/resource2.txt
@@ -0,0 +1 @@
+two resource
diff --git a/setuptools/_vendor/importlib_resources/tests/namespacedata01/binary.file b/setuptools/_vendor/importlib_resources/tests/namespacedata01/binary.file
new file mode 100644
index 00000000..eaf36c1d
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/namespacedata01/binary.file
Binary files differ
diff --git a/setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-16.file b/setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-16.file
new file mode 100644
index 00000000..2cb77229
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-16.file
Binary files differ
diff --git a/setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-8.file b/setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-8.file
new file mode 100644
index 00000000..1c0132ad
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/namespacedata01/utf-8.file
@@ -0,0 +1 @@
+Hello, UTF-8 world!
diff --git a/setuptools/_vendor/importlib_resources/tests/test_compatibilty_files.py b/setuptools/_vendor/importlib_resources/tests/test_compatibilty_files.py
new file mode 100644
index 00000000..d92c7c56
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_compatibilty_files.py
@@ -0,0 +1,102 @@
+import io
+import unittest
+
+import importlib_resources as resources
+
+from importlib_resources._adapters import (
+ CompatibilityFiles,
+ wrap_spec,
+)
+
+from . import util
+
+
+class CompatibilityFilesTests(unittest.TestCase):
+ @property
+ def package(self):
+ bytes_data = io.BytesIO(b'Hello, world!')
+ return util.create_package(
+ file=bytes_data,
+ path='some_path',
+ contents=('a', 'b', 'c'),
+ )
+
+ @property
+ def files(self):
+ return resources.files(self.package)
+
+ def test_spec_path_iter(self):
+ self.assertEqual(
+ sorted(path.name for path in self.files.iterdir()),
+ ['a', 'b', 'c'],
+ )
+
+ def test_child_path_iter(self):
+ self.assertEqual(list((self.files / 'a').iterdir()), [])
+
+ def test_orphan_path_iter(self):
+ self.assertEqual(list((self.files / 'a' / 'a').iterdir()), [])
+ self.assertEqual(list((self.files / 'a' / 'a' / 'a').iterdir()), [])
+
+ def test_spec_path_is(self):
+ self.assertFalse(self.files.is_file())
+ self.assertFalse(self.files.is_dir())
+
+ def test_child_path_is(self):
+ self.assertTrue((self.files / 'a').is_file())
+ self.assertFalse((self.files / 'a').is_dir())
+
+ def test_orphan_path_is(self):
+ self.assertFalse((self.files / 'a' / 'a').is_file())
+ self.assertFalse((self.files / 'a' / 'a').is_dir())
+ self.assertFalse((self.files / 'a' / 'a' / 'a').is_file())
+ self.assertFalse((self.files / 'a' / 'a' / 'a').is_dir())
+
+ def test_spec_path_name(self):
+ self.assertEqual(self.files.name, 'testingpackage')
+
+ def test_child_path_name(self):
+ self.assertEqual((self.files / 'a').name, 'a')
+
+ def test_orphan_path_name(self):
+ self.assertEqual((self.files / 'a' / 'b').name, 'b')
+ self.assertEqual((self.files / 'a' / 'b' / 'c').name, 'c')
+
+ def test_spec_path_open(self):
+ self.assertEqual(self.files.read_bytes(), b'Hello, world!')
+ self.assertEqual(self.files.read_text(), 'Hello, world!')
+
+ def test_child_path_open(self):
+ self.assertEqual((self.files / 'a').read_bytes(), b'Hello, world!')
+ self.assertEqual((self.files / 'a').read_text(), 'Hello, world!')
+
+ def test_orphan_path_open(self):
+ with self.assertRaises(FileNotFoundError):
+ (self.files / 'a' / 'b').read_bytes()
+ with self.assertRaises(FileNotFoundError):
+ (self.files / 'a' / 'b' / 'c').read_bytes()
+
+ def test_open_invalid_mode(self):
+ with self.assertRaises(ValueError):
+ self.files.open('0')
+
+ def test_orphan_path_invalid(self):
+ with self.assertRaises(ValueError):
+ CompatibilityFiles.OrphanPath()
+
+ def test_wrap_spec(self):
+ spec = wrap_spec(self.package)
+ self.assertIsInstance(spec.loader.get_resource_reader(None), CompatibilityFiles)
+
+
+class CompatibilityFilesNoReaderTests(unittest.TestCase):
+ @property
+ def package(self):
+ return util.create_package_from_loader(None)
+
+ @property
+ def files(self):
+ return resources.files(self.package)
+
+ def test_spec_path_joinpath(self):
+ self.assertIsInstance(self.files / 'a', CompatibilityFiles.OrphanPath)
diff --git a/setuptools/_vendor/importlib_resources/tests/test_contents.py b/setuptools/_vendor/importlib_resources/tests/test_contents.py
new file mode 100644
index 00000000..525568e8
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_contents.py
@@ -0,0 +1,43 @@
+import unittest
+import importlib_resources as resources
+
+from . import data01
+from . import util
+
+
+class ContentsTests:
+ expected = {
+ '__init__.py',
+ 'binary.file',
+ 'subdirectory',
+ 'utf-16.file',
+ 'utf-8.file',
+ }
+
+ def test_contents(self):
+ contents = {path.name for path in resources.files(self.data).iterdir()}
+ assert self.expected <= contents
+
+
+class ContentsDiskTests(ContentsTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class ContentsZipTests(ContentsTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+class ContentsNamespaceTests(ContentsTests, unittest.TestCase):
+ expected = {
+ # no __init__ because of namespace design
+ # no subdirectory as incidental difference in fixture
+ 'binary.file',
+ 'utf-16.file',
+ 'utf-8.file',
+ }
+
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
diff --git a/setuptools/_vendor/importlib_resources/tests/test_files.py b/setuptools/_vendor/importlib_resources/tests/test_files.py
new file mode 100644
index 00000000..2676b49e
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_files.py
@@ -0,0 +1,46 @@
+import typing
+import unittest
+
+import importlib_resources as resources
+from importlib_resources.abc import Traversable
+from . import data01
+from . import util
+
+
+class FilesTests:
+ def test_read_bytes(self):
+ files = resources.files(self.data)
+ actual = files.joinpath('utf-8.file').read_bytes()
+ assert actual == b'Hello, UTF-8 world!\n'
+
+ def test_read_text(self):
+ files = resources.files(self.data)
+ actual = files.joinpath('utf-8.file').read_text(encoding='utf-8')
+ assert actual == 'Hello, UTF-8 world!\n'
+
+ @unittest.skipUnless(
+ hasattr(typing, 'runtime_checkable'),
+ "Only suitable when typing supports runtime_checkable",
+ )
+ def test_traversable(self):
+ assert isinstance(resources.files(self.data), Traversable)
+
+
+class OpenDiskTests(FilesTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class OpenZipTests(FilesTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+class OpenNamespaceTests(FilesTests, unittest.TestCase):
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/setuptools/_vendor/importlib_resources/tests/test_open.py b/setuptools/_vendor/importlib_resources/tests/test_open.py
new file mode 100644
index 00000000..87b42c3d
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_open.py
@@ -0,0 +1,81 @@
+import unittest
+
+import importlib_resources as resources
+from . import data01
+from . import util
+
+
+class CommonBinaryTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ target = resources.files(package).joinpath(path)
+ with target.open('rb'):
+ pass
+
+
+class CommonTextTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ target = resources.files(package).joinpath(path)
+ with target.open():
+ pass
+
+
+class OpenTests:
+ def test_open_binary(self):
+ target = resources.files(self.data) / 'binary.file'
+ with target.open('rb') as fp:
+ result = fp.read()
+ self.assertEqual(result, b'\x00\x01\x02\x03')
+
+ def test_open_text_default_encoding(self):
+ target = resources.files(self.data) / 'utf-8.file'
+ with target.open() as fp:
+ result = fp.read()
+ self.assertEqual(result, 'Hello, UTF-8 world!\n')
+
+ def test_open_text_given_encoding(self):
+ target = resources.files(self.data) / 'utf-16.file'
+ with target.open(encoding='utf-16', errors='strict') as fp:
+ result = fp.read()
+ self.assertEqual(result, 'Hello, UTF-16 world!\n')
+
+ def test_open_text_with_errors(self):
+ # Raises UnicodeError without the 'errors' argument.
+ target = resources.files(self.data) / 'utf-16.file'
+ with target.open(encoding='utf-8', errors='strict') as fp:
+ self.assertRaises(UnicodeError, fp.read)
+ with target.open(encoding='utf-8', errors='ignore') as fp:
+ result = fp.read()
+ self.assertEqual(
+ result,
+ 'H\x00e\x00l\x00l\x00o\x00,\x00 '
+ '\x00U\x00T\x00F\x00-\x001\x006\x00 '
+ '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00',
+ )
+
+ def test_open_binary_FileNotFoundError(self):
+ target = resources.files(self.data) / 'does-not-exist'
+ self.assertRaises(FileNotFoundError, target.open, 'rb')
+
+ def test_open_text_FileNotFoundError(self):
+ target = resources.files(self.data) / 'does-not-exist'
+ self.assertRaises(FileNotFoundError, target.open)
+
+
+class OpenDiskTests(OpenTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class OpenDiskNamespaceTests(OpenTests, unittest.TestCase):
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
+
+
+class OpenZipTests(OpenTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/setuptools/_vendor/importlib_resources/tests/test_path.py b/setuptools/_vendor/importlib_resources/tests/test_path.py
new file mode 100644
index 00000000..4f4d3943
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_path.py
@@ -0,0 +1,64 @@
+import io
+import unittest
+
+import importlib_resources as resources
+from . import data01
+from . import util
+
+
+class CommonTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ with resources.as_file(resources.files(package).joinpath(path)):
+ pass
+
+
+class PathTests:
+ def test_reading(self):
+ # Path should be readable.
+ # Test also implicitly verifies the returned object is a pathlib.Path
+ # instance.
+ target = resources.files(self.data) / 'utf-8.file'
+ with resources.as_file(target) as path:
+ self.assertTrue(path.name.endswith("utf-8.file"), repr(path))
+ # pathlib.Path.read_text() was introduced in Python 3.5.
+ with path.open('r', encoding='utf-8') as file:
+ text = file.read()
+ self.assertEqual('Hello, UTF-8 world!\n', text)
+
+
+class PathDiskTests(PathTests, unittest.TestCase):
+ data = data01
+
+ def test_natural_path(self):
+ """
+ Guarantee the internal implementation detail that
+ file-system-backed resources do not get the tempdir
+ treatment.
+ """
+ target = resources.files(self.data) / 'utf-8.file'
+ with resources.as_file(target) as path:
+ assert 'data' in str(path)
+
+
+class PathMemoryTests(PathTests, unittest.TestCase):
+ def setUp(self):
+ file = io.BytesIO(b'Hello, UTF-8 world!\n')
+ self.addCleanup(file.close)
+ self.data = util.create_package(
+ file=file, path=FileNotFoundError("package exists only in memory")
+ )
+ self.data.__spec__.origin = None
+ self.data.__spec__.has_location = False
+
+
+class PathZipTests(PathTests, util.ZipSetup, unittest.TestCase):
+ def test_remove_in_context_manager(self):
+ # It is not an error if the file that was temporarily stashed on the
+ # file system is removed inside the `with` stanza.
+ target = resources.files(self.data) / 'utf-8.file'
+ with resources.as_file(target) as path:
+ path.unlink()
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/setuptools/_vendor/importlib_resources/tests/test_read.py b/setuptools/_vendor/importlib_resources/tests/test_read.py
new file mode 100644
index 00000000..41dd6db5
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_read.py
@@ -0,0 +1,76 @@
+import unittest
+import importlib_resources as resources
+
+from . import data01
+from . import util
+from importlib import import_module
+
+
+class CommonBinaryTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ resources.files(package).joinpath(path).read_bytes()
+
+
+class CommonTextTests(util.CommonTests, unittest.TestCase):
+ def execute(self, package, path):
+ resources.files(package).joinpath(path).read_text()
+
+
+class ReadTests:
+ def test_read_bytes(self):
+ result = resources.files(self.data).joinpath('binary.file').read_bytes()
+ self.assertEqual(result, b'\0\1\2\3')
+
+ def test_read_text_default_encoding(self):
+ result = resources.files(self.data).joinpath('utf-8.file').read_text()
+ self.assertEqual(result, 'Hello, UTF-8 world!\n')
+
+ def test_read_text_given_encoding(self):
+ result = (
+ resources.files(self.data)
+ .joinpath('utf-16.file')
+ .read_text(encoding='utf-16')
+ )
+ self.assertEqual(result, 'Hello, UTF-16 world!\n')
+
+ def test_read_text_with_errors(self):
+ # Raises UnicodeError without the 'errors' argument.
+ target = resources.files(self.data) / 'utf-16.file'
+ self.assertRaises(UnicodeError, target.read_text, encoding='utf-8')
+ result = target.read_text(encoding='utf-8', errors='ignore')
+ self.assertEqual(
+ result,
+ 'H\x00e\x00l\x00l\x00o\x00,\x00 '
+ '\x00U\x00T\x00F\x00-\x001\x006\x00 '
+ '\x00w\x00o\x00r\x00l\x00d\x00!\x00\n\x00',
+ )
+
+
+class ReadDiskTests(ReadTests, unittest.TestCase):
+ data = data01
+
+
+class ReadZipTests(ReadTests, util.ZipSetup, unittest.TestCase):
+ def test_read_submodule_resource(self):
+ submodule = import_module('ziptestdata.subdirectory')
+ result = resources.files(submodule).joinpath('binary.file').read_bytes()
+ self.assertEqual(result, b'\0\1\2\3')
+
+ def test_read_submodule_resource_by_name(self):
+ result = (
+ resources.files('ziptestdata.subdirectory')
+ .joinpath('binary.file')
+ .read_bytes()
+ )
+ self.assertEqual(result, b'\0\1\2\3')
+
+
+class ReadNamespaceTests(ReadTests, unittest.TestCase):
+ def setUp(self):
+ from . import namespacedata01
+
+ self.data = namespacedata01
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/setuptools/_vendor/importlib_resources/tests/test_reader.py b/setuptools/_vendor/importlib_resources/tests/test_reader.py
new file mode 100644
index 00000000..16841a50
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_reader.py
@@ -0,0 +1,128 @@
+import os.path
+import sys
+import pathlib
+import unittest
+
+from importlib import import_module
+from importlib_resources.readers import MultiplexedPath, NamespaceReader
+
+
+class MultiplexedPathTest(unittest.TestCase):
+ @classmethod
+ def setUpClass(cls):
+ path = pathlib.Path(__file__).parent / 'namespacedata01'
+ cls.folder = str(path)
+
+ def test_init_no_paths(self):
+ with self.assertRaises(FileNotFoundError):
+ MultiplexedPath()
+
+ def test_init_file(self):
+ with self.assertRaises(NotADirectoryError):
+ MultiplexedPath(os.path.join(self.folder, 'binary.file'))
+
+ def test_iterdir(self):
+ contents = {path.name for path in MultiplexedPath(self.folder).iterdir()}
+ try:
+ contents.remove('__pycache__')
+ except (KeyError, ValueError):
+ pass
+ self.assertEqual(contents, {'binary.file', 'utf-16.file', 'utf-8.file'})
+
+ def test_iterdir_duplicate(self):
+ data01 = os.path.abspath(os.path.join(__file__, '..', 'data01'))
+ contents = {
+ path.name for path in MultiplexedPath(self.folder, data01).iterdir()
+ }
+ for remove in ('__pycache__', '__init__.pyc'):
+ try:
+ contents.remove(remove)
+ except (KeyError, ValueError):
+ pass
+ self.assertEqual(
+ contents,
+ {'__init__.py', 'binary.file', 'subdirectory', 'utf-16.file', 'utf-8.file'},
+ )
+
+ def test_is_dir(self):
+ self.assertEqual(MultiplexedPath(self.folder).is_dir(), True)
+
+ def test_is_file(self):
+ self.assertEqual(MultiplexedPath(self.folder).is_file(), False)
+
+ def test_open_file(self):
+ path = MultiplexedPath(self.folder)
+ with self.assertRaises(FileNotFoundError):
+ path.read_bytes()
+ with self.assertRaises(FileNotFoundError):
+ path.read_text()
+ with self.assertRaises(FileNotFoundError):
+ path.open()
+
+ def test_join_path(self):
+ prefix = os.path.abspath(os.path.join(__file__, '..'))
+ data01 = os.path.join(prefix, 'data01')
+ path = MultiplexedPath(self.folder, data01)
+ self.assertEqual(
+ str(path.joinpath('binary.file'))[len(prefix) + 1 :],
+ os.path.join('namespacedata01', 'binary.file'),
+ )
+ self.assertEqual(
+ str(path.joinpath('subdirectory'))[len(prefix) + 1 :],
+ os.path.join('data01', 'subdirectory'),
+ )
+ self.assertEqual(
+ str(path.joinpath('imaginary'))[len(prefix) + 1 :],
+ os.path.join('namespacedata01', 'imaginary'),
+ )
+
+ def test_repr(self):
+ self.assertEqual(
+ repr(MultiplexedPath(self.folder)),
+ f"MultiplexedPath('{self.folder}')",
+ )
+
+ def test_name(self):
+ self.assertEqual(
+ MultiplexedPath(self.folder).name,
+ os.path.basename(self.folder),
+ )
+
+
+class NamespaceReaderTest(unittest.TestCase):
+ site_dir = str(pathlib.Path(__file__).parent)
+
+ @classmethod
+ def setUpClass(cls):
+ sys.path.append(cls.site_dir)
+
+ @classmethod
+ def tearDownClass(cls):
+ sys.path.remove(cls.site_dir)
+
+ def test_init_error(self):
+ with self.assertRaises(ValueError):
+ NamespaceReader(['path1', 'path2'])
+
+ def test_resource_path(self):
+ namespacedata01 = import_module('namespacedata01')
+ reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
+
+ root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
+ self.assertEqual(
+ reader.resource_path('binary.file'), os.path.join(root, 'binary.file')
+ )
+ self.assertEqual(
+ reader.resource_path('imaginary'), os.path.join(root, 'imaginary')
+ )
+
+ def test_files(self):
+ namespacedata01 = import_module('namespacedata01')
+ reader = NamespaceReader(namespacedata01.__spec__.submodule_search_locations)
+ root = os.path.abspath(os.path.join(__file__, '..', 'namespacedata01'))
+ self.assertIsInstance(reader.files(), MultiplexedPath)
+ self.assertEqual(repr(reader.files()), f"MultiplexedPath('{root}')")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/setuptools/_vendor/importlib_resources/tests/test_resource.py b/setuptools/_vendor/importlib_resources/tests/test_resource.py
new file mode 100644
index 00000000..5affd8b0
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/test_resource.py
@@ -0,0 +1,252 @@
+import sys
+import unittest
+import importlib_resources as resources
+import uuid
+import pathlib
+
+from . import data01
+from . import zipdata01, zipdata02
+from . import util
+from importlib import import_module
+from ._compat import import_helper, unlink
+
+
+class ResourceTests:
+ # Subclasses are expected to set the `data` attribute.
+
+ def test_is_file_exists(self):
+ target = resources.files(self.data) / 'binary.file'
+ self.assertTrue(target.is_file())
+
+ def test_is_file_missing(self):
+ target = resources.files(self.data) / 'not-a-file'
+ self.assertFalse(target.is_file())
+
+ def test_is_dir(self):
+ target = resources.files(self.data) / 'subdirectory'
+ self.assertFalse(target.is_file())
+ self.assertTrue(target.is_dir())
+
+
+class ResourceDiskTests(ResourceTests, unittest.TestCase):
+ def setUp(self):
+ self.data = data01
+
+
+class ResourceZipTests(ResourceTests, util.ZipSetup, unittest.TestCase):
+ pass
+
+
+def names(traversable):
+ return {item.name for item in traversable.iterdir()}
+
+
+class ResourceLoaderTests(unittest.TestCase):
+ def test_resource_contents(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C']
+ )
+ self.assertEqual(names(resources.files(package)), {'A', 'B', 'C'})
+
+ def test_is_file(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ )
+ self.assertTrue(resources.files(package).joinpath('B').is_file())
+
+ def test_is_dir(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ )
+ self.assertTrue(resources.files(package).joinpath('D').is_dir())
+
+ def test_resource_missing(self):
+ package = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C', 'D/E', 'D/F']
+ )
+ self.assertFalse(resources.files(package).joinpath('Z').is_file())
+
+
+class ResourceCornerCaseTests(unittest.TestCase):
+ def test_package_has_no_reader_fallback(self):
+ # Test odd ball packages which:
+ # 1. Do not have a ResourceReader as a loader
+ # 2. Are not on the file system
+ # 3. Are not in a zip file
+ module = util.create_package(
+ file=data01, path=data01.__file__, contents=['A', 'B', 'C']
+ )
+ # Give the module a dummy loader.
+ module.__loader__ = object()
+ # Give the module a dummy origin.
+ module.__file__ = '/path/which/shall/not/be/named'
+ module.__spec__.loader = module.__loader__
+ module.__spec__.origin = module.__file__
+ self.assertFalse(resources.files(module).joinpath('A').is_file())
+
+
+class ResourceFromZipsTest01(util.ZipSetupBase, unittest.TestCase):
+ ZIP_MODULE = zipdata01 # type: ignore
+
+ def test_is_submodule_resource(self):
+ submodule = import_module('ziptestdata.subdirectory')
+ self.assertTrue(resources.files(submodule).joinpath('binary.file').is_file())
+
+ def test_read_submodule_resource_by_name(self):
+ self.assertTrue(
+ resources.files('ziptestdata.subdirectory')
+ .joinpath('binary.file')
+ .is_file()
+ )
+
+ def test_submodule_contents(self):
+ submodule = import_module('ziptestdata.subdirectory')
+ self.assertEqual(
+ names(resources.files(submodule)), {'__init__.py', 'binary.file'}
+ )
+
+ def test_submodule_contents_by_name(self):
+ self.assertEqual(
+ names(resources.files('ziptestdata.subdirectory')),
+ {'__init__.py', 'binary.file'},
+ )
+
+
+class ResourceFromZipsTest02(util.ZipSetupBase, unittest.TestCase):
+ ZIP_MODULE = zipdata02 # type: ignore
+
+ def test_unrelated_contents(self):
+ """
+ Test thata zip with two unrelated subpackages return
+ distinct resources. Ref python/importlib_resources#44.
+ """
+ self.assertEqual(
+ names(resources.files('ziptestdata.one')),
+ {'__init__.py', 'resource1.txt'},
+ )
+ self.assertEqual(
+ names(resources.files('ziptestdata.two')),
+ {'__init__.py', 'resource2.txt'},
+ )
+
+
+class DeletingZipsTest(unittest.TestCase):
+ """Having accessed resources in a zip file should not keep an open
+ reference to the zip.
+ """
+
+ ZIP_MODULE = zipdata01
+
+ def setUp(self):
+ modules = import_helper.modules_setup()
+ self.addCleanup(import_helper.modules_cleanup, *modules)
+
+ data_path = pathlib.Path(self.ZIP_MODULE.__file__)
+ data_dir = data_path.parent
+ self.source_zip_path = data_dir / 'ziptestdata.zip'
+ self.zip_path = pathlib.Path(f'{uuid.uuid4()}.zip').absolute()
+ self.zip_path.write_bytes(self.source_zip_path.read_bytes())
+ sys.path.append(str(self.zip_path))
+ self.data = import_module('ziptestdata')
+
+ def tearDown(self):
+ try:
+ sys.path.remove(str(self.zip_path))
+ except ValueError:
+ pass
+
+ try:
+ del sys.path_importer_cache[str(self.zip_path)]
+ del sys.modules[self.data.__name__]
+ except KeyError:
+ pass
+
+ try:
+ unlink(self.zip_path)
+ except OSError:
+ # If the test fails, this will probably fail too
+ pass
+
+ def test_iterdir_does_not_keep_open(self):
+ c = [item.name for item in resources.files('ziptestdata').iterdir()]
+ self.zip_path.unlink()
+ del c
+
+ def test_is_file_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('binary.file').is_file()
+ self.zip_path.unlink()
+ del c
+
+ def test_is_file_failure_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('not-present').is_file()
+ self.zip_path.unlink()
+ del c
+
+ @unittest.skip("Desired but not supported.")
+ def test_as_file_does_not_keep_open(self): # pragma: no cover
+ c = resources.as_file(resources.files('ziptestdata') / 'binary.file')
+ self.zip_path.unlink()
+ del c
+
+ def test_entered_path_does_not_keep_open(self):
+ # This is what certifi does on import to make its bundle
+ # available for the process duration.
+ c = resources.as_file(
+ resources.files('ziptestdata') / 'binary.file'
+ ).__enter__()
+ self.zip_path.unlink()
+ del c
+
+ def test_read_binary_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('binary.file').read_bytes()
+ self.zip_path.unlink()
+ del c
+
+ def test_read_text_does_not_keep_open(self):
+ c = resources.files('ziptestdata').joinpath('utf-8.file').read_text()
+ self.zip_path.unlink()
+ del c
+
+
+class ResourceFromNamespaceTest01(unittest.TestCase):
+ site_dir = str(pathlib.Path(__file__).parent)
+
+ @classmethod
+ def setUpClass(cls):
+ sys.path.append(cls.site_dir)
+
+ @classmethod
+ def tearDownClass(cls):
+ sys.path.remove(cls.site_dir)
+
+ def test_is_submodule_resource(self):
+ self.assertTrue(
+ resources.files(import_module('namespacedata01'))
+ .joinpath('binary.file')
+ .is_file()
+ )
+
+ def test_read_submodule_resource_by_name(self):
+ self.assertTrue(
+ resources.files('namespacedata01').joinpath('binary.file').is_file()
+ )
+
+ def test_submodule_contents(self):
+ contents = names(resources.files(import_module('namespacedata01')))
+ try:
+ contents.remove('__pycache__')
+ except KeyError:
+ pass
+ self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
+
+ def test_submodule_contents_by_name(self):
+ contents = names(resources.files('namespacedata01'))
+ try:
+ contents.remove('__pycache__')
+ except KeyError:
+ pass
+ self.assertEqual(contents, {'binary.file', 'utf-8.file', 'utf-16.file'})
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/setuptools/_vendor/importlib_resources/tests/update-zips.py b/setuptools/_vendor/importlib_resources/tests/update-zips.py
new file mode 100644
index 00000000..9ef0224c
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/update-zips.py
@@ -0,0 +1,53 @@
+"""
+Generate the zip test data files.
+
+Run to build the tests/zipdataNN/ziptestdata.zip files from
+files in tests/dataNN.
+
+Replaces the file with the working copy, but does commit anything
+to the source repo.
+"""
+
+import contextlib
+import os
+import pathlib
+import zipfile
+
+
+def main():
+ """
+ >>> from unittest import mock
+ >>> monkeypatch = getfixture('monkeypatch')
+ >>> monkeypatch.setattr(zipfile, 'ZipFile', mock.MagicMock())
+ >>> print(); main() # print workaround for bpo-32509
+ <BLANKLINE>
+ ...data01... -> ziptestdata/...
+ ...
+ ...data02... -> ziptestdata/...
+ ...
+ """
+ suffixes = '01', '02'
+ tuple(map(generate, suffixes))
+
+
+def generate(suffix):
+ root = pathlib.Path(__file__).parent.relative_to(os.getcwd())
+ zfpath = root / f'zipdata{suffix}/ziptestdata.zip'
+ with zipfile.ZipFile(zfpath, 'w') as zf:
+ for src, rel in walk(root / f'data{suffix}'):
+ dst = 'ziptestdata' / pathlib.PurePosixPath(rel.as_posix())
+ print(src, '->', dst)
+ zf.write(src, dst)
+
+
+def walk(datapath):
+ for dirpath, dirnames, filenames in os.walk(datapath):
+ with contextlib.suppress(KeyError):
+ dirnames.remove('__pycache__')
+ for filename in filenames:
+ res = pathlib.Path(dirpath) / filename
+ rel = res.relative_to(datapath)
+ yield res, rel
+
+
+__name__ == '__main__' and main()
diff --git a/setuptools/_vendor/importlib_resources/tests/util.py b/setuptools/_vendor/importlib_resources/tests/util.py
new file mode 100644
index 00000000..c6d83e4b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/util.py
@@ -0,0 +1,178 @@
+import abc
+import importlib
+import io
+import sys
+import types
+from pathlib import Path, PurePath
+
+from . import data01
+from . import zipdata01
+from ..abc import ResourceReader
+from ._compat import import_helper
+
+
+from importlib.machinery import ModuleSpec
+
+
+class Reader(ResourceReader):
+ def __init__(self, **kwargs):
+ vars(self).update(kwargs)
+
+ def get_resource_reader(self, package):
+ return self
+
+ def open_resource(self, path):
+ self._path = path
+ if isinstance(self.file, Exception):
+ raise self.file
+ return self.file
+
+ def resource_path(self, path_):
+ self._path = path_
+ if isinstance(self.path, Exception):
+ raise self.path
+ return self.path
+
+ def is_resource(self, path_):
+ self._path = path_
+ if isinstance(self.path, Exception):
+ raise self.path
+
+ def part(entry):
+ return entry.split('/')
+
+ return any(
+ len(parts) == 1 and parts[0] == path_ for parts in map(part, self._contents)
+ )
+
+ def contents(self):
+ if isinstance(self.path, Exception):
+ raise self.path
+ yield from self._contents
+
+
+def create_package_from_loader(loader, is_package=True):
+ name = 'testingpackage'
+ module = types.ModuleType(name)
+ spec = ModuleSpec(name, loader, origin='does-not-exist', is_package=is_package)
+ module.__spec__ = spec
+ module.__loader__ = loader
+ return module
+
+
+def create_package(file=None, path=None, is_package=True, contents=()):
+ return create_package_from_loader(
+ Reader(file=file, path=path, _contents=contents),
+ is_package,
+ )
+
+
+class CommonTests(metaclass=abc.ABCMeta):
+ """
+ Tests shared by test_open, test_path, and test_read.
+ """
+
+ @abc.abstractmethod
+ def execute(self, package, path):
+ """
+ Call the pertinent legacy API function (e.g. open_text, path)
+ on package and path.
+ """
+
+ def test_package_name(self):
+ # Passing in the package name should succeed.
+ self.execute(data01.__name__, 'utf-8.file')
+
+ def test_package_object(self):
+ # Passing in the package itself should succeed.
+ self.execute(data01, 'utf-8.file')
+
+ def test_string_path(self):
+ # Passing in a string for the path should succeed.
+ path = 'utf-8.file'
+ self.execute(data01, path)
+
+ def test_pathlib_path(self):
+ # Passing in a pathlib.PurePath object for the path should succeed.
+ path = PurePath('utf-8.file')
+ self.execute(data01, path)
+
+ def test_importing_module_as_side_effect(self):
+ # The anchor package can already be imported.
+ del sys.modules[data01.__name__]
+ self.execute(data01.__name__, 'utf-8.file')
+
+ def test_non_package_by_name(self):
+ # The anchor package cannot be a module.
+ with self.assertRaises(TypeError):
+ self.execute(__name__, 'utf-8.file')
+
+ def test_non_package_by_package(self):
+ # The anchor package cannot be a module.
+ with self.assertRaises(TypeError):
+ module = sys.modules['importlib_resources.tests.util']
+ self.execute(module, 'utf-8.file')
+
+ def test_missing_path(self):
+ # Attempting to open or read or request the path for a
+ # non-existent path should succeed if open_resource
+ # can return a viable data stream.
+ bytes_data = io.BytesIO(b'Hello, world!')
+ package = create_package(file=bytes_data, path=FileNotFoundError())
+ self.execute(package, 'utf-8.file')
+ self.assertEqual(package.__loader__._path, 'utf-8.file')
+
+ def test_extant_path(self):
+ # Attempting to open or read or request the path when the
+ # path does exist should still succeed. Does not assert
+ # anything about the result.
+ bytes_data = io.BytesIO(b'Hello, world!')
+ # any path that exists
+ path = __file__
+ package = create_package(file=bytes_data, path=path)
+ self.execute(package, 'utf-8.file')
+ self.assertEqual(package.__loader__._path, 'utf-8.file')
+
+ def test_useless_loader(self):
+ package = create_package(file=FileNotFoundError(), path=FileNotFoundError())
+ with self.assertRaises(FileNotFoundError):
+ self.execute(package, 'utf-8.file')
+
+
+class ZipSetupBase:
+ ZIP_MODULE = None
+
+ @classmethod
+ def setUpClass(cls):
+ data_path = Path(cls.ZIP_MODULE.__file__)
+ data_dir = data_path.parent
+ cls._zip_path = str(data_dir / 'ziptestdata.zip')
+ sys.path.append(cls._zip_path)
+ cls.data = importlib.import_module('ziptestdata')
+
+ @classmethod
+ def tearDownClass(cls):
+ try:
+ sys.path.remove(cls._zip_path)
+ except ValueError:
+ pass
+
+ try:
+ del sys.path_importer_cache[cls._zip_path]
+ del sys.modules[cls.data.__name__]
+ except KeyError:
+ pass
+
+ try:
+ del cls.data
+ del cls._zip_path
+ except AttributeError:
+ pass
+
+ def setUp(self):
+ modules = import_helper.modules_setup()
+ self.addCleanup(import_helper.modules_cleanup, *modules)
+
+
+class ZipSetup(ZipSetupBase):
+ ZIP_MODULE = zipdata01 # type: ignore
diff --git a/setuptools/_vendor/importlib_resources/tests/zipdata01/__init__.py b/setuptools/_vendor/importlib_resources/tests/zipdata01/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/zipdata01/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zip b/setuptools/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zip
new file mode 100644
index 00000000..9a3bb073
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/zipdata01/ziptestdata.zip
Binary files differ
diff --git a/setuptools/_vendor/importlib_resources/tests/zipdata02/__init__.py b/setuptools/_vendor/importlib_resources/tests/zipdata02/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/zipdata02/__init__.py
diff --git a/setuptools/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zip b/setuptools/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zip
new file mode 100644
index 00000000..d63ff512
--- /dev/null
+++ b/setuptools/_vendor/importlib_resources/tests/zipdata02/ziptestdata.zip
Binary files differ
diff --git a/setuptools/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/jaraco.context-4.1.1.dist-info/LICENSE b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/setuptools/_vendor/jaraco.context-4.1.1.dist-info/METADATA b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/METADATA
new file mode 100644
index 00000000..908711b7
--- /dev/null
+++ b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/METADATA
@@ -0,0 +1,52 @@
+Metadata-Version: 2.1
+Name: jaraco.context
+Version: 4.1.1
+Summary: Context managers by jaraco
+Home-page: https://github.com/jaraco/jaraco.context
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.6
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.context.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.context.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.context
+
+.. image:: https://github.com/jaraco/jaraco.context/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.context/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracocontext/badge/?version=latest
+ :target: https://jaracocontext.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+
diff --git a/setuptools/_vendor/jaraco.context-4.1.1.dist-info/RECORD b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/RECORD
new file mode 100644
index 00000000..f40d48c7
--- /dev/null
+++ b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/RECORD
@@ -0,0 +1,8 @@
+jaraco.context-4.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+jaraco.context-4.1.1.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+jaraco.context-4.1.1.dist-info/METADATA,sha256=bvqDGCk6Z7TkohUqr5XZm19SbF9mVxrtXjN6uF_BAMQ,2031
+jaraco.context-4.1.1.dist-info/RECORD,,
+jaraco.context-4.1.1.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+jaraco.context-4.1.1.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+jaraco/__pycache__/context.cpython-310.pyc,,
+jaraco/context.py,sha256=7X1tpCLc5EN45iWGzGcsH0Unx62REIkvtRvglj0SiUA,5420
diff --git a/setuptools/_vendor/jaraco.context-4.1.1.dist-info/WHEEL b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt
new file mode 100644
index 00000000..f6205a5f
--- /dev/null
+++ b/setuptools/_vendor/jaraco.context-4.1.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/METADATA b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/METADATA
new file mode 100644
index 00000000..12dfbdd0
--- /dev/null
+++ b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/METADATA
@@ -0,0 +1,58 @@
+Metadata-Version: 2.1
+Name: jaraco.functools
+Version: 3.5.0
+Summary: Functools like those found in stdlib
+Home-page: https://github.com/jaraco/jaraco.functools
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.7
+License-File: LICENSE
+Requires-Dist: more-itertools
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: jaraco.classes ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.functools.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.functools.svg
+
+.. image:: https://img.shields.io/travis/jaraco/jaraco.functools/master.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.functools
+
+.. image:: https://github.com/jaraco/jaraco.functools/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.functools/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracofunctools/badge/?version=latest
+ :target: https://jaracofunctools.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+Additional functools in the spirit of stdlib's functools.
+
+
diff --git a/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/RECORD b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/RECORD
new file mode 100644
index 00000000..fbda3d1f
--- /dev/null
+++ b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/RECORD
@@ -0,0 +1,8 @@
+jaraco.functools-3.5.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+jaraco.functools-3.5.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+jaraco.functools-3.5.0.dist-info/METADATA,sha256=cE9C7u9bo_GjLAuw4nML67a25kUaPDiHn4j03lG4jd0,2276
+jaraco.functools-3.5.0.dist-info/RECORD,,
+jaraco.functools-3.5.0.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+jaraco.functools-3.5.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+jaraco/__pycache__/functools.cpython-310.pyc,,
+jaraco/functools.py,sha256=PtEHbXZstgVJrwje4GvJOsz5pEbgslOcgEn2EJNpr2c,13494
diff --git a/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt
new file mode 100644
index 00000000..f6205a5f
--- /dev/null
+++ b/setuptools/_vendor/jaraco.functools-3.5.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/setuptools/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/jaraco.text-3.7.0.dist-info/LICENSE b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/setuptools/_vendor/jaraco.text-3.7.0.dist-info/METADATA b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/METADATA
new file mode 100644
index 00000000..615a50a4
--- /dev/null
+++ b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/METADATA
@@ -0,0 +1,55 @@
+Metadata-Version: 2.1
+Name: jaraco.text
+Version: 3.7.0
+Summary: Module for text manipulation
+Home-page: https://github.com/jaraco/jaraco.text
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.6
+License-File: LICENSE
+Requires-Dist: jaraco.functools
+Requires-Dist: jaraco.context (>=4.1)
+Requires-Dist: importlib-resources ; python_version < "3.9"
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.text.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.text.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/jaraco.text
+
+.. image:: https://github.com/jaraco/jaraco.text/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/jaraco.text/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. image:: https://readthedocs.org/projects/jaracotext/badge/?version=latest
+ :target: https://jaracotext.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+
diff --git a/setuptools/_vendor/jaraco.text-3.7.0.dist-info/RECORD b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/RECORD
new file mode 100644
index 00000000..916ad7d3
--- /dev/null
+++ b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/RECORD
@@ -0,0 +1,10 @@
+jaraco.text-3.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+jaraco.text-3.7.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+jaraco.text-3.7.0.dist-info/METADATA,sha256=5mcR1dY0cJNrM-VIkAFkpjOgvgzmq6nM1GfD0gwTIhs,2136
+jaraco.text-3.7.0.dist-info/RECORD,,
+jaraco.text-3.7.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+jaraco.text-3.7.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+jaraco.text-3.7.0.dist-info/top_level.txt,sha256=0JnN3LfXH4LIRfXL-QFOGCJzQWZO3ELx4R1d_louoQM,7
+jaraco/text/Lorem ipsum.txt,sha256=N_7c_79zxOufBY9HZ3yzMgOkNv-TkOTTio4BydrSjgs,1335
+jaraco/text/__init__.py,sha256=I56MW2ZFwPrYXIxzqxMBe2A1t-T4uZBgEgAKe9-JoqM,15538
+jaraco/text/__pycache__/__init__.cpython-310.pyc,,
diff --git a/setuptools/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/REQUESTED
diff --git a/setuptools/_vendor/jaraco.text-3.7.0.dist-info/WHEEL b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/WHEEL
new file mode 100644
index 00000000..becc9a66
--- /dev/null
+++ b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt
new file mode 100644
index 00000000..f6205a5f
--- /dev/null
+++ b/setuptools/_vendor/jaraco.text-3.7.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/setuptools/_vendor/jaraco/__init__.py b/setuptools/_vendor/jaraco/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/jaraco/__init__.py
diff --git a/setuptools/_vendor/jaraco/context.py b/setuptools/_vendor/jaraco/context.py
new file mode 100644
index 00000000..87a4e3dc
--- /dev/null
+++ b/setuptools/_vendor/jaraco/context.py
@@ -0,0 +1,213 @@
+import os
+import subprocess
+import contextlib
+import functools
+import tempfile
+import shutil
+import operator
+
+
+@contextlib.contextmanager
+def pushd(dir):
+ orig = os.getcwd()
+ os.chdir(dir)
+ try:
+ yield dir
+ finally:
+ os.chdir(orig)
+
+
+@contextlib.contextmanager
+def tarball_context(url, target_dir=None, runner=None, pushd=pushd):
+ """
+ Get a tarball, extract it, change to that directory, yield, then
+ clean up.
+ `runner` is the function to invoke commands.
+ `pushd` is a context manager for changing the directory.
+ """
+ if target_dir is None:
+ target_dir = os.path.basename(url).replace('.tar.gz', '').replace('.tgz', '')
+ if runner is None:
+ runner = functools.partial(subprocess.check_call, shell=True)
+ # 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.
+ runner('mkdir {target_dir}'.format(**vars()))
+ try:
+ getter = 'wget {url} -O -'
+ extract = 'tar x{compression} --strip-components=1 -C {target_dir}'
+ cmd = ' | '.join((getter, extract))
+ runner(cmd.format(compression=infer_compression(url), **vars()))
+ with pushd(target_dir):
+ yield target_dir
+ finally:
+ runner('rm -Rf {target_dir}'.format(**vars()))
+
+
+def infer_compression(url):
+ """
+ Given a URL or filename, infer the compression code for tar.
+ """
+ # 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.
+ """
+ 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
+
+
+@contextlib.contextmanager
+def null():
+ yield
+
+
+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
+
+ >>> 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()
+ """
diff --git a/setuptools/_vendor/jaraco/functools.py b/setuptools/_vendor/jaraco/functools.py
new file mode 100644
index 00000000..bbd8b29f
--- /dev/null
+++ b/setuptools/_vendor/jaraco/functools.py
@@ -0,0 +1,525 @@
+import functools
+import time
+import inspect
+import collections
+import types
+import itertools
+
+import setuptools.extern.more_itertools
+
+from typing import Callable, TypeVar
+
+
+CallableT = TypeVar("CallableT", bound=Callable[..., object])
+
+
+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 method_caller(method_name, *args, **kwargs):
+ """
+ Return a function that will call a named method on the
+ target object with optional positional and keyword
+ arguments.
+
+ >>> lower = method_caller('lower')
+ >>> lower('MyString')
+ 'mystring'
+ """
+
+ def call_method(target):
+ func = getattr(target, method_name)
+ return func(*args, **kwargs)
+
+ return call_method
+
+
+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: CallableT,
+ cache_wrapper: Callable[
+ [CallableT], CallableT
+ ] = functools.lru_cache(), # type: ignore[assignment]
+) -> CallableT:
+ """
+ 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: object, *args: object, **kwargs: object) -> object:
+ # it's the first call, replace the method with a cached, bound method
+ bound_method: CallableT = types.MethodType( # type: ignore[assignment]
+ 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 # type: ignore[attr-defined]
+
+ return ( # type: ignore[return-value]
+ _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
+
+ 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 call_aside(f, *args, **kwargs):
+ """
+ Call a function for its side effect after initialization.
+
+ >>> @call_aside
+ ... def func(): print("called")
+ called
+ >>> func()
+ called
+
+ Use functools.partial to pass parameters to the initial call
+
+ >>> @functools.partial(call_aside, 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, type=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
+
+
+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 attempt 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 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
diff --git a/setuptools/_vendor/jaraco/text/Lorem ipsum.txt b/setuptools/_vendor/jaraco/text/Lorem ipsum.txt
new file mode 100644
index 00000000..986f944b
--- /dev/null
+++ b/setuptools/_vendor/jaraco/text/Lorem ipsum.txt
@@ -0,0 +1,2 @@
+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.
diff --git a/setuptools/_vendor/jaraco/text/__init__.py b/setuptools/_vendor/jaraco/text/__init__.py
new file mode 100644
index 00000000..a0306d5f
--- /dev/null
+++ b/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.
+ <BLANKLINE>
+ 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 <bold>stormy</bold> day in paradise'))
+ A stormy day in paradise
+
+ >>> print(simple_html_strip('Somebody <!-- do not --> tell the truth.'))
+ Somebody tell the truth.
+
+ >>> print(simple_html_strip('What about<br/>\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/setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER b/setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE b/setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE
new file mode 100644
index 00000000..0a523bec
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Erik Rose
+
+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/setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA b/setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA
new file mode 100644
index 00000000..bdaee655
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA
@@ -0,0 +1,462 @@
+Metadata-Version: 2.1
+Name: more-itertools
+Version: 8.8.0
+Summary: More routines for operating on iterables, beyond itertools
+Home-page: https://github.com/more-itertools/more-itertools
+Author: Erik Rose
+Author-email: erikrose@grinchcentral.com
+License: MIT
+Keywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries
+Requires-Python: >=3.5
+Description-Content-Type: text/x-rst
+
+==============
+More Itertools
+==============
+
+.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest
+ :target: https://more-itertools.readthedocs.io/en/stable/
+
+Python's ``itertools`` library is a gem - you can compose elegant solutions
+for a variety of problems with the functions it provides. In ``more-itertools``
+we collect additional building blocks, recipes, and routines for working with
+Python iterables.
+
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Grouping | `chunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_, |
+| | `ichunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_, |
+| | `sliced <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliced>`_, |
+| | `distribute <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distribute>`_, |
+| | `divide <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.divide>`_, |
+| | `split_at <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_at>`_, |
+| | `split_before <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_before>`_, |
+| | `split_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_after>`_, |
+| | `split_into <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_into>`_, |
+| | `split_when <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_when>`_, |
+| | `bucket <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.bucket>`_, |
+| | `unzip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unzip>`_, |
+| | `grouper <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper>`_, |
+| | `partition <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partition>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Lookahead and lookback | `spy <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy>`_, |
+| | `peekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable>`_, |
+| | `seekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.seekable>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Windowing | `windowed <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed>`_, |
+| | `substrings <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings>`_, |
+| | `substrings_indexes <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings_indexes>`_, |
+| | `stagger <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.stagger>`_, |
+| | `windowed_complete <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed_complete>`_, |
+| | `pairwise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pairwise>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Augmenting | `count_cycle <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.count_cycle>`_, |
+| | `intersperse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.intersperse>`_, |
+| | `padded <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padded>`_, |
+| | `mark_ends <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.mark_ends>`_, |
+| | `repeat_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_last>`_, |
+| | `adjacent <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.adjacent>`_, |
+| | `groupby_transform <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.groupby_transform>`_, |
+| | `padnone <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padnone>`_, |
+| | `ncycles <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ncycles>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combining | `collapse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse>`_, |
+| | `sort_together <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sort_together>`_, |
+| | `interleave <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_, |
+| | `interleave_longest <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_, |
+| | `zip_offset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_, |
+| | `zip_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_, |
+| | `dotproduct <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_, |
+| | `convolve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.convolve>`_, |
+| | `flatten <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_, |
+| | `roundrobin <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_, |
+| | `prepend <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.prepend>`_, |
+| | `value_chain <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.value_chain>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Summarizing | `ilen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ilen>`_, |
+| | `unique_to_each <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_to_each>`_, |
+| | `sample <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sample>`_, |
+| | `consecutive_groups <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consecutive_groups>`_, |
+| | `run_length <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.run_length>`_, |
+| | `map_reduce <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_reduce>`_, |
+| | `exactly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.exactly_n>`_, |
+| | `is_sorted <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_sorted>`_, |
+| | `all_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_equal>`_, |
+| | `all_unique <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_unique>`_, |
+| | `first_true <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first_true>`_, |
+| | `quantify <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.quantify>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Selecting | `islice_extended <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.islice_extended>`_, |
+| | `first <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first>`_, |
+| | `last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.last>`_, |
+| | `one <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one>`_, |
+| | `only <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.only>`_, |
+| | `strip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strip>`_, |
+| | `lstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.lstrip>`_, |
+| | `rstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rstrip>`_, |
+| | `filter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.filter_except>`_ |
+| | `map_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_except>`_ |
+| | `nth_or_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_or_last>`_, |
+| | `nth <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth>`_, |
+| | `take <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.take>`_, |
+| | `tail <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tail>`_, |
+| | `unique_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertoo ls.unique_everseen>`_, |
+| | `unique_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_justseen>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combinatorics | `distinct_permutations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_permutations>`_, |
+| | `distinct_combinations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_combinations>`_, |
+| | `circular_shifts <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.circular_shifts>`_, |
+| | `partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partitions>`_, |
+| | `set_partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.set_partitions>`_, |
+| | `product_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.product_index>`_, |
+| | `combination_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.combination_index>`_, |
+| | `permutation_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.permutation_index>`_, |
+| | `powerset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset>`_, |
+| | `random_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product>`_, |
+| | `random_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation>`_, |
+| | `random_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination>`_, |
+| | `random_combination_with_replacement <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination_with_replacement>`_, |
+| | `nth_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_product>`_ |
+| | `nth_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_permutation>`_ |
+| | `nth_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_combination>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Wrapping | `always_iterable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_, |
+| | `always_reversible <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_reversible>`_, |
+| | `countable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.countable>`_, |
+| | `consumer <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consumer>`_, |
+| | `with_iter <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.with_iter>`_, |
+| | `iter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iter_except>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Others | `locate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.locate>`_, |
+| | `rlocate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rlocate>`_, |
+| | `replace <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.replace>`_, |
+| | `numeric_range <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.numeric_range>`_, |
+| | `side_effect <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.side_effect>`_, |
+| | `iterate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iterate>`_, |
+| | `difference <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.difference>`_, |
+| | `make_decorator <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.make_decorator>`_, |
+| | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, |
+| | `time_limited <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.time_limited>`_, |
+| | `consume <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume>`_, |
+| | `tabulate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tabulate>`_, |
+| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+
+Getting started
+===============
+
+To get started, install the library with `pip <https://pip.pypa.io/en/stable/>`_:
+
+.. code-block:: shell
+
+ pip install more-itertools
+
+The recipes from the `itertools docs <https://docs.python.org/3/library/itertools.html#itertools-recipes>`_
+are included in the top-level package:
+
+.. code-block:: python
+
+ >>> from more_itertools import flatten
+ >>> iterable = [(0, 1), (2, 3)]
+ >>> list(flatten(iterable))
+ [0, 1, 2, 3]
+
+Several new recipes are available as well:
+
+.. code-block:: python
+
+ >>> from more_itertools import chunked
+ >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8]
+ >>> list(chunked(iterable, 3))
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
+
+ >>> from more_itertools import spy
+ >>> iterable = (x * x for x in range(1, 6))
+ >>> head, iterable = spy(iterable, n=3)
+ >>> list(head)
+ [1, 4, 9]
+ >>> list(iterable)
+ [1, 4, 9, 16, 25]
+
+
+
+For the full listing of functions, see the `API documentation <https://more-itertools.readthedocs.io/en/stable/api.html>`_.
+
+
+Links elsewhere
+===============
+
+Blog posts about ``more-itertools``:
+
+* `Yo, I heard you like decorators <https://www.bbayles.com/index/decorator_factory>`__
+* `Tour of Python Itertools <https://martinheinz.dev/blog/16>`__ (`Alternate <https://dev.to/martinheinz/tour-of-python-itertools-4122>`__)
+
+
+Development
+===========
+
+``more-itertools`` is maintained by `@erikrose <https://github.com/erikrose>`_
+and `@bbayles <https://github.com/bbayles>`_, with help from `many others <https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
+If you have a problem or suggestion, please file a bug or pull request in this
+repository. Thanks for contributing!
+
+
+Version History
+===============
+
+
+ :noindex:
+
+8.8.0
+-----
+
+* New functions
+ * countable (thanks to krzysieq)
+
+* Changes to existing functions
+ * split_before was updated to handle empy collections (thanks to TiunovNN)
+ * unique_everseen got a performance boost (thanks to Numerlor)
+ * The type hint for value_chain was corrected (thanks to vr2262)
+
+8.7.0
+-----
+
+* New functions
+ * convolve (from the Python itertools docs)
+ * product_index, combination_index, and permutation_index (thanks to N8Brooks)
+ * value_chain (thanks to jenstroeger)
+
+* Changes to existing functions
+ * distinct_combinations now uses a non-recursive algorithm (thanks to knutdrand)
+ * pad_none is now the preferred name for padnone, though the latter remains available.
+ * pairwise will now use the Python standard library implementation on Python 3.10+
+ * sort_together now accepts a ``key`` argument (thanks to brianmaissy)
+ * seekable now has a ``peek`` method, and can indicate whether the iterator it's wrapping is exhausted (thanks to gsakkis)
+ * time_limited can now indicate whether its iterator has expired (thanks to roysmith)
+ * The implementation of unique_everseen was improved (thanks to plammens)
+
+* Other changes:
+ * Various documentation updates (thanks to cthoyt, Evantm, and cyphase)
+
+8.6.0
+-----
+
+* New itertools
+ * all_unique (thanks to brianmaissy)
+ * nth_product and nth_permutation (thanks to N8Brooks)
+
+* Changes to existing itertools
+ * chunked and sliced now accept a ``strict`` parameter (thanks to shlomif and jtwool)
+
+* Other changes
+ * Python 3.5 has reached its end of life and is no longer supported.
+ * Python 3.9 is officially supported.
+ * Various documentation fixes (thanks to timgates42)
+
+8.5.0
+-----
+
+* New itertools
+ * windowed_complete (thanks to MarcinKonowalczyk)
+
+* Changes to existing itertools:
+ * The is_sorted implementation was improved (thanks to cool-RR)
+ * The groupby_transform now accepts a ``reducefunc`` parameter.
+ * The last implementation was improved (thanks to brianmaissy)
+
+* Other changes
+ * Various documentation fixes (thanks to craigrosie, samuelstjean, PiCT0)
+ * The tests for distinct_combinations were improved (thanks to Minabsapi)
+ * Automated tests now run on GitHub Actions. All commits now check:
+ * That unit tests pass
+ * That the examples in docstrings work
+ * That test coverage remains high (using `coverage`)
+ * For linting errors (using `flake8`)
+ * For consistent style (using `black`)
+ * That the type stubs work (using `mypy`)
+ * That the docs build correctly (using `sphinx`)
+ * That packages build correctly (using `twine`)
+
+8.4.0
+-----
+
+* New itertools
+ * mark_ends (thanks to kalekundert)
+ * is_sorted
+
+* Changes to existing itertools:
+ * islice_extended can now be used with real slices (thanks to cool-RR)
+ * The implementations for filter_except and map_except were improved (thanks to SergBobrovsky)
+
+* Other changes
+ * Automated tests now enforce code style (using `black <https://github.com/psf/black>`__)
+ * The various signatures of islice_extended and numeric_range now appear in the docs (thanks to dsfulf)
+ * The test configuration for mypy was updated (thanks to blueyed)
+
+
+8.3.0
+-----
+
+* New itertools
+ * zip_equal (thanks to frankier and alexmojaki)
+
+* Changes to existing itertools:
+ * split_at, split_before, split_after, and split_when all got a ``maxsplit`` paramter (thanks to jferard and ilai-deutel)
+ * split_at now accepts a ``keep_separator`` parameter (thanks to jferard)
+ * distinct_permutations can now generate ``r``-length permutations (thanks to SergBobrovsky and ilai-deutel)
+ * The windowed implementation was improved (thanks to SergBobrovsky)
+ * The spy implementation was improved (thanks to has2k1)
+
+* Other changes
+ * Type stubs are now tested with ``stubtest`` (thanks to ilai-deutel)
+ * Tests now run with ``python -m unittest`` instead of ``python setup.py test`` (thanks to jdufresne)
+
+8.2.0
+-----
+
+* Bug fixes
+ * The .pyi files for typing were updated. (thanks to blueyed and ilai-deutel)
+
+* Changes to existing itertools:
+ * numeric_range now behaves more like the built-in range. (thanks to jferard)
+ * bucket now allows for enumerating keys. (thanks to alexchandel)
+ * sliced now should now work for numpy arrays. (thanks to sswingle)
+ * seekable now has a ``maxlen`` parameter.
+
+8.1.0
+-----
+
+* Bug fixes
+ * partition works with ``pred=None`` again. (thanks to MSeifert04)
+
+* New itertools
+ * sample (thanks to tommyod)
+ * nth_or_last (thanks to d-ryzhikov)
+
+* Changes to existing itertools:
+ * The implementation for divide was improved. (thanks to jferard)
+
+8.0.2
+-----
+
+* Bug fixes
+ * The type stub files are now part of the wheel distribution (thanks to keisheiled)
+
+8.0.1
+-----
+
+* Bug fixes
+ * The type stub files now work for functions imported from the
+ root package (thanks to keisheiled)
+
+8.0.0
+-----
+
+* New itertools and other additions
+ * This library now ships type hints for use with mypy.
+ (thanks to ilai-deutel for the implementation, and to gabbard and fmagin for assistance)
+ * split_when (thanks to jferard)
+ * repeat_last (thanks to d-ryzhikov)
+
+* Changes to existing itertools:
+ * The implementation for set_partitions was improved. (thanks to jferard)
+ * partition was optimized for expensive predicates. (thanks to stevecj)
+ * unique_everseen and groupby_transform were re-factored. (thanks to SergBobrovsky)
+ * The implementation for difference was improved. (thanks to Jabbey92)
+
+* Other changes
+ * Python 3.4 has reached its end of life and is no longer supported.
+ * Python 3.8 is officially supported. (thanks to jdufresne)
+ * The ``collate`` function has been deprecated.
+ It raises a ``DeprecationWarning`` if used, and will be removed in a future release.
+ * one and only now provide more informative error messages. (thanks to gabbard)
+ * Unit tests were moved outside of the main package (thanks to jdufresne)
+ * Various documentation fixes (thanks to kriomant, gabbard, jdufresne)
+
+
+7.2.0
+-----
+
+* New itertools
+ * distinct_combinations
+ * set_partitions (thanks to kbarrett)
+ * filter_except
+ * map_except
+
+7.1.0
+-----
+
+* New itertools
+ * ichunked (thanks davebelais and youtux)
+ * only (thanks jaraco)
+
+* Changes to existing itertools:
+ * numeric_range now supports ranges specified by
+ ``datetime.datetime`` and ``datetime.timedelta`` objects (thanks to MSeifert04 for tests).
+ * difference now supports an *initial* keyword argument.
+
+
+* Other changes
+ * Various documentation fixes (thanks raimon49, pylang)
+
+7.0.0
+-----
+
+* New itertools:
+ * time_limited
+ * partitions (thanks to rominf and Saluev)
+ * substrings_indexes (thanks to rominf)
+
+* Changes to existing itertools:
+ * collapse now treats ``bytes`` objects the same as ``str`` objects. (thanks to Sweenpet)
+
+The major version update is due to the change in the default behavior of
+collapse. It now treats ``bytes`` objects the same as ``str`` objects.
+This aligns its behavior with always_iterable.
+
+.. code-block:: python
+
+ >>> from more_itertools import collapse
+ >>> iterable = [[1, 2], b'345', [6]]
+ >>> print(list(collapse(iterable)))
+ [1, 2, b'345', 6]
+
+6.0.0
+-----
+
+* Major changes:
+ * Python 2.7 is no longer supported. The 5.0.0 release will be the last
+ version targeting Python 2.7.
+ * All future releases will target the active versions of Python 3.
+ As of 2019, those are Python 3.4 and above.
+ * The ``six`` library is no longer a dependency.
+ * The accumulate function is no longer part of this library. You
+ may import a better version from the standard ``itertools`` module.
+
+* Changes to existing itertools:
+ * The order of the parameters in grouper have changed to match
+ the latest recipe in the itertools documentation. Use of the old order
+ will be supported in this release, but emit a ``DeprecationWarning``.
+ The legacy behavior will be dropped in a future release. (thanks to jaraco)
+ * distinct_permutations was improved (thanks to jferard - see also `permutations with unique values <https://stackoverflow.com/questions/6284396/permutations-with-unique-values>`_ at StackOverflow.)
+ * An unused parameter was removed from substrings. (thanks to pylang)
+
+* Other changes:
+ * The docs for unique_everseen were improved. (thanks to jferard and MSeifert04)
+ * Several Python 2-isms were removed. (thanks to jaraco, MSeifert04, and hugovk)
+
+
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD b/setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD
new file mode 100644
index 00000000..36ffbd86
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD
@@ -0,0 +1,17 @@
+more_itertools-8.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+more_itertools-8.8.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053
+more_itertools-8.8.0.dist-info/METADATA,sha256=Gke9w7RnfiAvveik_iBBrzd0RjrDhsQ8uRYNBJdo4qQ,40482
+more_itertools-8.8.0.dist-info/RECORD,,
+more_itertools-8.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+more_itertools-8.8.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
+more_itertools-8.8.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15
+more_itertools/__init__.py,sha256=C7sXffHTXM3P-iaLPPfqfmDoxOflQMJLcM7ed9p3jak,82
+more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43
+more_itertools/__pycache__/__init__.cpython-310.pyc,,
+more_itertools/__pycache__/more.cpython-310.pyc,,
+more_itertools/__pycache__/recipes.cpython-310.pyc,,
+more_itertools/more.py,sha256=DlZa8v6JihVwfQ5zHidOA-xDE0orcQIUyxVnCaUoDKE,117968
+more_itertools/more.pyi,sha256=r32pH2raBC1zih3evK4fyvAXvrUamJqc6dgV7QCRL_M,14977
+more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+more_itertools/recipes.py,sha256=UkNkrsZyqiwgLHANBTmvMhCvaNSvSNYhyOpz_Jc55DY,16256
+more_itertools/recipes.pyi,sha256=9BpeKd5_qalYVSnuHfqPSCfoGgqnQY2Xu9pNwrDlHU8,3551
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED b/setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL b/setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL
new file mode 100644
index 00000000..385faab0
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.36.2)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt b/setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt
new file mode 100644
index 00000000..a5035bef
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+more_itertools
diff --git a/setuptools/_vendor/more_itertools/more.py b/setuptools/_vendor/more_itertools/more.py
index 0f7d282a..e6fca4d4 100644
--- a/setuptools/_vendor/more_itertools/more.py
+++ b/setuptools/_vendor/more_itertools/more.py
@@ -2,7 +2,6 @@ import warnings
from collections import Counter, defaultdict, deque, abc
from collections.abc import Sequence
-from concurrent.futures import ThreadPoolExecutor
from functools import partial, reduce, wraps
from heapq import merge, heapify, heapreplace, heappop
from itertools import (
@@ -3454,7 +3453,7 @@ class callback_iter:
self._aborted = False
self._future = None
self._wait_seconds = wait_seconds
- self._executor = ThreadPoolExecutor(max_workers=1)
+ self._executor = __import__("concurrent.futures").futures.ThreadPoolExecutor(max_workers=1)
self._iterator = self._reader()
def __enter__(self):
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER b/setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA b/setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA
new file mode 100644
index 00000000..db6e12f2
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA
@@ -0,0 +1,157 @@
+Metadata-Version: 2.1
+Name: ordered-set
+Version: 3.1.1
+Summary: A MutableSet that remembers its order, so that every entry has an index.
+Home-page: https://github.com/LuminosoInsight/ordered-set
+Maintainer: Robyn Speer
+Maintainer-email: rspeer@luminoso.com
+License: MIT-LICENSE
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=2.7
+Description-Content-Type: text/markdown
+License-File: MIT-LICENSE
+
+[![Travis](https://img.shields.io/travis/LuminosoInsight/ordered-set/master.svg?label=Travis%20CI)](https://travis-ci.org/LuminosoInsight/ordered-set)
+[![Codecov](https://codecov.io/github/LuminosoInsight/ordered-set/badge.svg?branch=master&service=github)](https://codecov.io/github/LuminosoInsight/ordered-set?branch=master)
+[![Pypi](https://img.shields.io/pypi/v/ordered-set.svg)](https://pypi.python.org/pypi/ordered-set)
+
+An OrderedSet is a mutable data structure that is a hybrid of a list and a set.
+It remembers the order of its entries, and every entry has an index number that
+can be looked up.
+
+
+## Usage examples
+
+An OrderedSet is created and used like a set:
+
+ >>> from ordered_set import OrderedSet
+
+ >>> letters = OrderedSet('abracadabra')
+
+ >>> letters
+ OrderedSet(['a', 'b', 'r', 'c', 'd'])
+
+ >>> 'r' in letters
+ True
+
+It is efficient to find the index of an entry in an OrderedSet, or find an
+entry by its index. To help with this use case, the `.add()` method returns
+the index of the added item, whether it was already in the set or not.
+
+ >>> letters.index('r')
+ 2
+
+ >>> letters[2]
+ 'r'
+
+ >>> letters.add('r')
+ 2
+
+ >>> letters.add('x')
+ 5
+
+OrderedSets implement the union (`|`), intersection (`&`), and difference (`-`)
+operators like sets do.
+
+ >>> letters |= OrderedSet('shazam')
+
+ >>> letters
+ OrderedSet(['a', 'b', 'r', 'c', 'd', 'x', 's', 'h', 'z', 'm'])
+
+ >>> letters & set('aeiou')
+ OrderedSet(['a'])
+
+ >>> letters -= 'abcd'
+
+ >>> letters
+ OrderedSet(['r', 'x', 's', 'h', 'z', 'm'])
+
+The `__getitem__()` and `index()` methods have been extended to accept any
+iterable except a string, returning a list, to perform NumPy-like "fancy
+indexing".
+
+ >>> letters = OrderedSet('abracadabra')
+
+ >>> letters[[0, 2, 3]]
+ ['a', 'r', 'c']
+
+ >>> letters.index(['a', 'r', 'c'])
+ [0, 2, 3]
+
+OrderedSet implements `__getstate__` and `__setstate__` so it can be pickled,
+and implements the abstract base classes `collections.MutableSet` and
+`collections.Sequence`.
+
+
+## Interoperability with NumPy and Pandas
+
+An OrderedSet can be used as a bi-directional mapping between a sparse
+vocabulary and dense index numbers. As of version 3.1, it accepts NumPy arrays
+of index numbers as well as lists.
+
+This combination of features makes OrderedSet a simple implementation of many
+of the things that `pandas.Index` is used for, and many of its operations are
+faster than the equivalent pandas operations.
+
+For further compatibility with pandas.Index, `get_loc` (the pandas method for
+looking up a single index) and `get_indexer` (the pandas method for fancy
+indexing in reverse) are both aliases for `index` (which handles both cases
+in OrderedSet).
+
+
+## Type hinting
+To use type hinting features install `ordered-set-stubs` package from
+[PyPI](https://pypi.org/project/ordered-set-stubs/):
+
+ $ pip install ordered-set-stubs
+
+
+## Authors
+
+OrderedSet was implemented by Robyn Speer. Jon Crall contributed changes and
+tests to make it fit the Python set API.
+
+
+## Comparisons
+
+The original implementation of OrderedSet was a [recipe posted to ActiveState
+Recipes][recipe] by Raymond Hettiger, released under the MIT license.
+
+[recipe]: https://code.activestate.com/recipes/576694-orderedset/
+
+Hettiger's implementation kept its content in a doubly-linked list referenced by a
+dict. As a result, looking up an item by its index was an O(N) operation, while
+deletion was O(1).
+
+This version makes different trade-offs for the sake of efficient lookups. Its
+content is a standard Python list instead of a doubly-linked list. This
+provides O(1) lookups by index at the expense of O(N) deletion, as well as
+slightly faster iteration.
+
+In Python 3.6 and later, the built-in `dict` type is inherently ordered. If you
+ignore the dictionary values, that also gives you a simple ordered set, with
+fast O(1) insertion, deletion, iteration and membership testing. However, `dict`
+does not provide the list-like random access features of OrderedSet. You
+would have to convert it to a list in O(N) to look up the index of an entry or
+look up an entry by its index.
+
+
+## Compatibility
+
+OrderedSet is automatically tested on Python 2.7, 3.4, 3.5, 3.6, and 3.7.
+We've checked more informally that it works on PyPy and PyPy3.
+
+
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE b/setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE
new file mode 100644
index 00000000..25117ef4
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018 Luminoso Technologies, Inc.
+
+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/setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD b/setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD
new file mode 100644
index 00000000..89579a07
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD
@@ -0,0 +1,9 @@
+__pycache__/ordered_set.cpython-310.pyc,,
+ordered_set-3.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+ordered_set-3.1.1.dist-info/METADATA,sha256=uGvfFaNmhcl69lGdHmyOXc30N3U6Jn8DByfh_VHEPpw,5359
+ordered_set-3.1.1.dist-info/MIT-LICENSE,sha256=TvRE7qUSUBcd0ols7wgNf3zDEEJWW7kv7WDRySrMBBE,1071
+ordered_set-3.1.1.dist-info/RECORD,,
+ordered_set-3.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ordered_set-3.1.1.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
+ordered_set-3.1.1.dist-info/top_level.txt,sha256=NTY2_aDi1Do9fl3Z9EmWPxasFkUeW2dzO2D3RDx5CfM,12
+ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED b/setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL b/setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL
new file mode 100644
index 00000000..0b18a281
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt b/setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt
new file mode 100644
index 00000000..1c191eef
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+ordered_set
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/INSTALLER b/setuptools/_vendor/packaging-21.2.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/LICENSE b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE
new file mode 100644
index 00000000..6f62d44e
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE
@@ -0,0 +1,3 @@
+This software is made available under the terms of *either* of the licenses
+found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made
+under the terms of *both* these licenses.
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE
new file mode 100644
index 00000000..f433b1a5
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD
new file mode 100644
index 00000000..42ce7b75
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD
@@ -0,0 +1,23 @@
+Copyright (c) Donald Stufft and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/METADATA b/setuptools/_vendor/packaging-21.2.dist-info/METADATA
new file mode 100644
index 00000000..e8ff54d7
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/METADATA
@@ -0,0 +1,446 @@
+Metadata-Version: 2.1
+Name: packaging
+Version: 21.2
+Summary: Core utilities for Python packages
+Home-page: https://github.com/pypa/packaging
+Author: Donald Stufft and individual contributors
+Author-email: donald@stufft.io
+License: BSD-2-Clause or Apache-2.0
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+License-File: LICENSE.APACHE
+License-File: LICENSE.BSD
+Requires-Dist: pyparsing (<3,>=2.0.2)
+
+packaging
+=========
+
+.. start-intro
+
+Reusable core utilities for various Python Packaging
+`interoperability specifications <https://packaging.python.org/specifications/>`_.
+
+This library provides utilities that implement the interoperability
+specifications which have clearly one correct behaviour (eg: :pep:`440`)
+or benefit greatly from having a single shared implementation (eg: :pep:`425`).
+
+.. end-intro
+
+The ``packaging`` project includes the following: version handling, specifiers,
+markers, requirements, tags, utilities.
+
+Documentation
+-------------
+
+The `documentation`_ provides information and the API for the following:
+
+- Version Handling
+- Specifiers
+- Markers
+- Requirements
+- Tags
+- Utilities
+
+Installation
+------------
+
+Use ``pip`` to install these utilities::
+
+ pip install packaging
+
+Discussion
+----------
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+You can also join ``#pypa`` on Freenode to ask questions or get involved.
+
+
+.. _`documentation`: https://packaging.pypa.io/
+.. _`issue tracker`: https://github.com/pypa/packaging/issues
+
+
+Code of Conduct
+---------------
+
+Everyone interacting in the packaging project's codebases, issue trackers, chat
+rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
+
+.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
+
+Contributing
+------------
+
+The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as
+well as how to report a potential security issue. The documentation for this
+project also covers information about `project development`_ and `security`_.
+
+.. _`project development`: https://packaging.pypa.io/en/latest/development/
+.. _`security`: https://packaging.pypa.io/en/latest/security/
+
+Project History
+---------------
+
+Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for
+recent changes and project history.
+
+.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/
+
+Changelog
+---------
+
+21.2 - 2021-10-29
+~~~~~~~~~~~~~~~~~
+
+* Update documentation entry for 21.1.
+
+21.1 - 2021-10-29
+~~~~~~~~~~~~~~~~~
+
+* Update pin to pyparsing to exclude 3.0.0.
+
+21.0 - 2021-07-03
+~~~~~~~~~~~~~~~~~
+
+* PEP 656: musllinux support (`#411 <https://github.com/pypa/packaging/issues/411>`__)
+* Drop support for Python 2.7, Python 3.4 and Python 3.5.
+* Replace distutils usage with sysconfig (`#396 <https://github.com/pypa/packaging/issues/396>`__)
+* Add support for zip files in ``parse_sdist_filename`` (`#429 <https://github.com/pypa/packaging/issues/429>`__)
+* Use cached ``_hash`` attribute to short-circuit tag equality comparisons (`#417 <https://github.com/pypa/packaging/issues/417>`__)
+* Specify the default value for the ``specifier`` argument to ``SpecifierSet`` (`#437 <https://github.com/pypa/packaging/issues/437>`__)
+* Proper keyword-only "warn" argument in packaging.tags (`#403 <https://github.com/pypa/packaging/issues/403>`__)
+* Correctly remove prerelease suffixes from ~= check (`#366 <https://github.com/pypa/packaging/issues/366>`__)
+* Fix type hints for ``Version.post`` and ``Version.dev`` (`#393 <https://github.com/pypa/packaging/issues/393>`__)
+* Use typing alias ``UnparsedVersion`` (`#398 <https://github.com/pypa/packaging/issues/398>`__)
+* Improve type inference for ``packaging.specifiers.filter()`` (`#430 <https://github.com/pypa/packaging/issues/430>`__)
+* Tighten the return type of ``canonicalize_version()`` (`#402 <https://github.com/pypa/packaging/issues/402>`__)
+
+20.9 - 2021-01-29
+~~~~~~~~~~~~~~~~~
+
+* Run `isort <https://pypi.org/project/isort/>`_ over the code base (`#377 <https://github.com/pypa/packaging/issues/377>`__)
+* Add support for the ``macosx_10_*_universal2`` platform tags (`#379 <https://github.com/pypa/packaging/issues/379>`__)
+* Introduce ``packaging.utils.parse_wheel_filename()`` and ``parse_sdist_filename()``
+ (`#387 <https://github.com/pypa/packaging/issues/387>`__ and `#389 <https://github.com/pypa/packaging/issues/389>`__)
+
+20.8 - 2020-12-11
+~~~~~~~~~~~~~~~~~
+
+* Revert back to setuptools for compatibility purposes for some Linux distros (`#363 <https://github.com/pypa/packaging/issues/363>`__)
+* Do not insert an underscore in wheel tags when the interpreter version number
+ is more than 2 digits (`#372 <https://github.com/pypa/packaging/issues/372>`__)
+
+20.7 - 2020-11-28
+~~~~~~~~~~~~~~~~~
+
+No unreleased changes.
+
+20.6 - 2020-11-28
+~~~~~~~~~~~~~~~~~
+
+.. note:: This release was subsequently yanked, and these changes were included in 20.7.
+
+* Fix flit configuration, to include LICENSE files (`#357 <https://github.com/pypa/packaging/issues/357>`__)
+* Make `intel` a recognized CPU architecture for the `universal` macOS platform tag (`#361 <https://github.com/pypa/packaging/issues/361>`__)
+* Add some missing type hints to `packaging.requirements` (issue:`350`)
+
+20.5 - 2020-11-27
+~~~~~~~~~~~~~~~~~
+
+* Officially support Python 3.9 (`#343 <https://github.com/pypa/packaging/issues/343>`__)
+* Deprecate the ``LegacyVersion`` and ``LegacySpecifier`` classes (`#321 <https://github.com/pypa/packaging/issues/321>`__)
+* Handle ``OSError`` on non-dynamic executables when attempting to resolve
+ the glibc version string.
+
+20.4 - 2020-05-19
+~~~~~~~~~~~~~~~~~
+
+* Canonicalize version before comparing specifiers. (`#282 <https://github.com/pypa/packaging/issues/282>`__)
+* Change type hint for ``canonicalize_name`` to return
+ ``packaging.utils.NormalizedName``.
+ This enables the use of static typing tools (like mypy) to detect mixing of
+ normalized and un-normalized names.
+
+20.3 - 2020-03-05
+~~~~~~~~~~~~~~~~~
+
+* Fix changelog for 20.2.
+
+20.2 - 2020-03-05
+~~~~~~~~~~~~~~~~~
+
+* Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8,
+ aarch64), to report the wrong bitness.
+
+20.1 - 2020-01-24
+~~~~~~~~~~~~~~~~~~~
+
+* Fix a bug caused by reuse of an exhausted iterator. (`#257 <https://github.com/pypa/packaging/issues/257>`__)
+
+20.0 - 2020-01-06
+~~~~~~~~~~~~~~~~~
+
+* Add type hints (`#191 <https://github.com/pypa/packaging/issues/191>`__)
+
+* Add proper trove classifiers for PyPy support (`#198 <https://github.com/pypa/packaging/issues/198>`__)
+
+* Scale back depending on ``ctypes`` for manylinux support detection (`#171 <https://github.com/pypa/packaging/issues/171>`__)
+
+* Use ``sys.implementation.name`` where appropriate for ``packaging.tags`` (`#193 <https://github.com/pypa/packaging/issues/193>`__)
+
+* Expand upon the API provided by ``packaging.tags``: ``interpreter_name()``, ``mac_platforms()``, ``compatible_tags()``, ``cpython_tags()``, ``generic_tags()`` (`#187 <https://github.com/pypa/packaging/issues/187>`__)
+
+* Officially support Python 3.8 (`#232 <https://github.com/pypa/packaging/issues/232>`__)
+
+* Add ``major``, ``minor``, and ``micro`` aliases to ``packaging.version.Version`` (`#226 <https://github.com/pypa/packaging/issues/226>`__)
+
+* Properly mark ``packaging`` has being fully typed by adding a `py.typed` file (`#226 <https://github.com/pypa/packaging/issues/226>`__)
+
+19.2 - 2019-09-18
+~~~~~~~~~~~~~~~~~
+
+* Remove dependency on ``attrs`` (`#178 <https://github.com/pypa/packaging/issues/178>`__, `#179 <https://github.com/pypa/packaging/issues/179>`__)
+
+* Use appropriate fallbacks for CPython ABI tag (`#181 <https://github.com/pypa/packaging/issues/181>`__, `#185 <https://github.com/pypa/packaging/issues/185>`__)
+
+* Add manylinux2014 support (`#186 <https://github.com/pypa/packaging/issues/186>`__)
+
+* Improve ABI detection (`#181 <https://github.com/pypa/packaging/issues/181>`__)
+
+* Properly handle debug wheels for Python 3.8 (`#172 <https://github.com/pypa/packaging/issues/172>`__)
+
+* Improve detection of debug builds on Windows (`#194 <https://github.com/pypa/packaging/issues/194>`__)
+
+19.1 - 2019-07-30
+~~~~~~~~~~~~~~~~~
+
+* Add the ``packaging.tags`` module. (`#156 <https://github.com/pypa/packaging/issues/156>`__)
+
+* Correctly handle two-digit versions in ``python_version`` (`#119 <https://github.com/pypa/packaging/issues/119>`__)
+
+
+19.0 - 2019-01-20
+~~~~~~~~~~~~~~~~~
+
+* Fix string representation of PEP 508 direct URL requirements with markers.
+
+* Better handling of file URLs
+
+ This allows for using ``file:///absolute/path``, which was previously
+ prevented due to the missing ``netloc``.
+
+ This allows for all file URLs that ``urlunparse`` turns back into the
+ original URL to be valid.
+
+
+18.0 - 2018-09-26
+~~~~~~~~~~~~~~~~~
+
+* Improve error messages when invalid requirements are given. (`#129 <https://github.com/pypa/packaging/issues/129>`__)
+
+
+17.1 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions.
+
+
+17.0 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Drop support for python 2.6, 3.2, and 3.3.
+
+* Define minimal pyparsing version to 2.0.2 (`#91 <https://github.com/pypa/packaging/issues/91>`__).
+
+* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to
+ ``Version`` and ``LegacyVersion`` (`#34 <https://github.com/pypa/packaging/issues/34>`__).
+
+* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to
+ make it easy to determine if a release is a development release.
+
+* Add ``utils.canonicalize_version`` to canonicalize version strings or
+ ``Version`` instances (`#121 <https://github.com/pypa/packaging/issues/121>`__).
+
+
+16.8 - 2016-10-29
+~~~~~~~~~~~~~~~~~
+
+* Fix markers that utilize ``in`` so that they render correctly.
+
+* Fix an erroneous test on Python RC releases.
+
+
+16.7 - 2016-04-23
+~~~~~~~~~~~~~~~~~
+
+* Add support for the deprecated ``python_implementation`` marker which was
+ an undocumented setuptools marker in addition to the newer markers.
+
+
+16.6 - 2016-03-29
+~~~~~~~~~~~~~~~~~
+
+* Add support for the deprecated, PEP 345 environment markers in addition to
+ the newer markers.
+
+
+16.5 - 2016-02-26
+~~~~~~~~~~~~~~~~~
+
+* Fix a regression in parsing requirements with whitespaces between the comma
+ separators.
+
+
+16.4 - 2016-02-22
+~~~~~~~~~~~~~~~~~
+
+* Fix a regression in parsing requirements like ``foo (==4)``.
+
+
+16.3 - 2016-02-21
+~~~~~~~~~~~~~~~~~
+
+* Fix a bug where ``packaging.requirements:Requirement`` was overly strict when
+ matching legacy requirements.
+
+
+16.2 - 2016-02-09
+~~~~~~~~~~~~~~~~~
+
+* Add a function that implements the name canonicalization from PEP 503.
+
+
+16.1 - 2016-02-07
+~~~~~~~~~~~~~~~~~
+
+* Implement requirement specifiers from PEP 508.
+
+
+16.0 - 2016-01-19
+~~~~~~~~~~~~~~~~~
+
+* Relicense so that packaging is available under *either* the Apache License,
+ Version 2.0 or a 2 Clause BSD license.
+
+* Support installation of packaging when only distutils is available.
+
+* Fix ``==`` comparison when there is a prefix and a local version in play.
+ (`#41 <https://github.com/pypa/packaging/issues/41>`__).
+
+* Implement environment markers from PEP 508.
+
+
+15.3 - 2015-08-01
+~~~~~~~~~~~~~~~~~
+
+* Normalize post-release spellings for rev/r prefixes. `#35 <https://github.com/pypa/packaging/issues/35>`__
+
+
+15.2 - 2015-05-13
+~~~~~~~~~~~~~~~~~
+
+* Fix an error where the arbitrary specifier (``===``) was not correctly
+ allowing pre-releases when it was being used.
+
+* Expose the specifier and version parts through properties on the
+ ``Specifier`` classes.
+
+* Allow iterating over the ``SpecifierSet`` to get access to all of the
+ ``Specifier`` instances.
+
+* Allow testing if a version is contained within a specifier via the ``in``
+ operator.
+
+
+15.1 - 2015-04-13
+~~~~~~~~~~~~~~~~~
+
+* Fix a logic error that was causing inconsistent answers about whether or not
+ a pre-release was contained within a ``SpecifierSet`` or not.
+
+
+15.0 - 2015-01-02
+~~~~~~~~~~~~~~~~~
+
+* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to
+ make it easy to determine if a release is a post release.
+
+* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make
+ it easy to get the public version without any pre or post release markers.
+
+* Support the update to PEP 440 which removed the implied ``!=V.*`` when using
+ either ``>V`` or ``<V`` and which instead special cased the handling of
+ pre-releases, post-releases, and local versions when using ``>V`` or ``<V``.
+
+
+14.5 - 2014-12-17
+~~~~~~~~~~~~~~~~~
+
+* Normalize release candidates as ``rc`` instead of ``c``.
+
+* Expose the ``VERSION_PATTERN`` constant, a regular expression matching
+ a valid version.
+
+
+14.4 - 2014-12-15
+~~~~~~~~~~~~~~~~~
+
+* Ensure that versions are normalized before comparison when used in a
+ specifier with a less than (``<``) or greater than (``>``) operator.
+
+
+14.3 - 2014-11-19
+~~~~~~~~~~~~~~~~~
+
+* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely
+ handle legacy specifiers as well as PEP 440 specifiers.
+
+* **BACKWARDS INCOMPATIBLE** Move the specifier support out of
+ ``packaging.version`` into ``packaging.specifiers``.
+
+
+14.2 - 2014-09-10
+~~~~~~~~~~~~~~~~~
+
+* Add prerelease support to ``Specifier``.
+* Remove the ability to do ``item in Specifier()`` and replace it with
+ ``Specifier().contains(item)`` in order to allow flags that signal if a
+ prerelease should be accepted or not.
+* Add a method ``Specifier().filter()`` which will take an iterable and returns
+ an iterable with items that do not match the specifier filtered out.
+
+
+14.1 - 2014-09-08
+~~~~~~~~~~~~~~~~~
+
+* Allow ``LegacyVersion`` and ``Version`` to be sorted together.
+* Add ``packaging.version.parse()`` to enable easily parsing a version string
+ as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440
+ validity.
+
+
+14.0 - 2014-09-05
+~~~~~~~~~~~~~~~~~
+
+* Initial release.
+
+
+.. _`master`: https://github.com/pypa/packaging/
+
+
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/RECORD b/setuptools/_vendor/packaging-21.2.dist-info/RECORD
new file mode 100644
index 00000000..ed2291ac
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/RECORD
@@ -0,0 +1,32 @@
+packaging-21.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+packaging-21.2.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+packaging-21.2.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+packaging-21.2.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+packaging-21.2.dist-info/METADATA,sha256=N4A8uSYrQwV9byem7YuI9OtVkbqiNzFlDhcDVT-suAo,14754
+packaging-21.2.dist-info/RECORD,,
+packaging-21.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+packaging-21.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+packaging-21.2.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10
+packaging/__about__.py,sha256=IIRHpOsJlJSgkjq1UoeBoMTqhvNp3gN9FyMb5Kf8El4,661
+packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497
+packaging/__pycache__/__about__.cpython-310.pyc,,
+packaging/__pycache__/__init__.cpython-310.pyc,,
+packaging/__pycache__/_manylinux.cpython-310.pyc,,
+packaging/__pycache__/_musllinux.cpython-310.pyc,,
+packaging/__pycache__/_structures.cpython-310.pyc,,
+packaging/__pycache__/markers.cpython-310.pyc,,
+packaging/__pycache__/requirements.cpython-310.pyc,,
+packaging/__pycache__/specifiers.cpython-310.pyc,,
+packaging/__pycache__/tags.cpython-310.pyc,,
+packaging/__pycache__/utils.cpython-310.pyc,,
+packaging/__pycache__/version.cpython-310.pyc,,
+packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488
+packaging/_musllinux.py,sha256=z5yeG1ygOPx4uUyLdqj-p8Dk5UBb5H_b0NIjW9yo8oA,4378
+packaging/_structures.py,sha256=TMiAgFbdUOPmIfDIfiHc3KFhSJ8kMjof2QS5I-2NyQ8,1629
+packaging/markers.py,sha256=Fygi3_eZnjQ-3VJizW5AhI5wvo0Hb6RMk4DidsKpOC0,8475
+packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+packaging/requirements.py,sha256=rjaGRCMepZS1mlYMjJ5Qh6rfq3gtsCRQUQmftGZ_bu8,4664
+packaging/specifiers.py,sha256=MZ-fYcNL3u7pNrt-6g2EQO7AbRXkjc-SPEYwXMQbLmc,30964
+packaging/tags.py,sha256=vGybAUQYlPKMcukzX_2e65fmafnFFuMbD25naYTEwtc,15710
+packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200
+packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/REQUESTED b/setuptools/_vendor/packaging-21.2.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/REQUESTED
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/WHEEL b/setuptools/_vendor/packaging-21.2.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/top_level.txt b/setuptools/_vendor/packaging-21.2.dist-info/top_level.txt
new file mode 100644
index 00000000..748809f7
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/top_level.txt
@@ -0,0 +1 @@
+packaging
diff --git a/setuptools/_vendor/packaging/__about__.py b/setuptools/_vendor/packaging/__about__.py
index 4d998578..c359122f 100644
--- a/setuptools/_vendor/packaging/__about__.py
+++ b/setuptools/_vendor/packaging/__about__.py
@@ -1,7 +1,6 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
__all__ = [
"__title__",
@@ -18,10 +17,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
-__version__ = "20.4"
+__version__ = "21.2"
__author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io"
__license__ = "BSD-2-Clause or Apache-2.0"
-__copyright__ = "Copyright 2014-2019 %s" % __author__
+__copyright__ = "2014-2019 %s" % __author__
diff --git a/setuptools/_vendor/packaging/__init__.py b/setuptools/_vendor/packaging/__init__.py
index a0cf67df..3c50c5dc 100644
--- a/setuptools/_vendor/packaging/__init__.py
+++ b/setuptools/_vendor/packaging/__init__.py
@@ -1,7 +1,6 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
from .__about__ import (
__author__,
diff --git a/setuptools/_vendor/packaging/_compat.py b/setuptools/_vendor/packaging/_compat.py
deleted file mode 100644
index e54bd4ed..00000000
--- a/setuptools/_vendor/packaging/_compat.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# This file is dual licensed under the terms of the Apache License, Version
-# 2.0, and the BSD License. See the LICENSE file in the root of this repository
-# for complete details.
-from __future__ import absolute_import, division, print_function
-
-import sys
-
-from ._typing import TYPE_CHECKING
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Any, Dict, Tuple, Type
-
-
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-# flake8: noqa
-
-if PY3:
- string_types = (str,)
-else:
- string_types = (basestring,)
-
-
-def with_metaclass(meta, *bases):
- # type: (Type[Any], Tuple[Type[Any], ...]) -> Any
- """
- Create a base class with a metaclass.
- """
- # This requires a bit of explanation: the basic idea is to make a dummy
- # metaclass for one level of class instantiation that replaces itself with
- # the actual metaclass.
- class metaclass(meta): # type: ignore
- def __new__(cls, name, this_bases, d):
- # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
- return meta(name, bases, d)
-
- return type.__new__(metaclass, "temporary_class", (), {})
diff --git a/setuptools/_vendor/packaging/_manylinux.py b/setuptools/_vendor/packaging/_manylinux.py
new file mode 100644
index 00000000..4c379aa6
--- /dev/null
+++ b/setuptools/_vendor/packaging/_manylinux.py
@@ -0,0 +1,301 @@
+import collections
+import functools
+import os
+import re
+import struct
+import sys
+import warnings
+from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple
+
+
+# Python does not provide platform information at sufficient granularity to
+# identify the architecture of the running executable in some cases, so we
+# determine it dynamically by reading the information from the running
+# process. This only applies on Linux, which uses the ELF format.
+class _ELFFileHeader:
+ # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
+ class _InvalidELFFileHeader(ValueError):
+ """
+ An invalid ELF file header was found.
+ """
+
+ ELF_MAGIC_NUMBER = 0x7F454C46
+ ELFCLASS32 = 1
+ ELFCLASS64 = 2
+ ELFDATA2LSB = 1
+ ELFDATA2MSB = 2
+ EM_386 = 3
+ EM_S390 = 22
+ EM_ARM = 40
+ EM_X86_64 = 62
+ EF_ARM_ABIMASK = 0xFF000000
+ EF_ARM_ABI_VER5 = 0x05000000
+ EF_ARM_ABI_FLOAT_HARD = 0x00000400
+
+ def __init__(self, file: IO[bytes]) -> None:
+ def unpack(fmt: str) -> int:
+ try:
+ data = file.read(struct.calcsize(fmt))
+ result: Tuple[int, ...] = struct.unpack(fmt, data)
+ except struct.error:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ return result[0]
+
+ self.e_ident_magic = unpack(">I")
+ if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_class = unpack("B")
+ if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_data = unpack("B")
+ if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_version = unpack("B")
+ self.e_ident_osabi = unpack("B")
+ self.e_ident_abiversion = unpack("B")
+ self.e_ident_pad = file.read(7)
+ format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
+ format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
+ format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
+ format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
+ self.e_type = unpack(format_h)
+ self.e_machine = unpack(format_h)
+ self.e_version = unpack(format_i)
+ self.e_entry = unpack(format_p)
+ self.e_phoff = unpack(format_p)
+ self.e_shoff = unpack(format_p)
+ self.e_flags = unpack(format_i)
+ self.e_ehsize = unpack(format_h)
+ self.e_phentsize = unpack(format_h)
+ self.e_phnum = unpack(format_h)
+ self.e_shentsize = unpack(format_h)
+ self.e_shnum = unpack(format_h)
+ self.e_shstrndx = unpack(format_h)
+
+
+def _get_elf_header() -> Optional[_ELFFileHeader]:
+ try:
+ with open(sys.executable, "rb") as f:
+ elf_header = _ELFFileHeader(f)
+ except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
+ return None
+ return elf_header
+
+
+def _is_linux_armhf() -> bool:
+ # hard-float ABI can be detected from the ELF header of the running
+ # process
+ # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
+ elf_header = _get_elf_header()
+ if elf_header is None:
+ return False
+ result = elf_header.e_ident_class == elf_header.ELFCLASS32
+ result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
+ result &= elf_header.e_machine == elf_header.EM_ARM
+ result &= (
+ elf_header.e_flags & elf_header.EF_ARM_ABIMASK
+ ) == elf_header.EF_ARM_ABI_VER5
+ result &= (
+ elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD
+ ) == elf_header.EF_ARM_ABI_FLOAT_HARD
+ return result
+
+
+def _is_linux_i686() -> bool:
+ elf_header = _get_elf_header()
+ if elf_header is None:
+ return False
+ result = elf_header.e_ident_class == elf_header.ELFCLASS32
+ result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
+ result &= elf_header.e_machine == elf_header.EM_386
+ return result
+
+
+def _have_compatible_abi(arch: str) -> bool:
+ if arch == "armv7l":
+ return _is_linux_armhf()
+ if arch == "i686":
+ return _is_linux_i686()
+ return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"}
+
+
+# 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:
+ # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
+ version_string = os.confstr("CS_GNU_LIBC_VERSION")
+ assert version_string is not None
+ _, version = version_string.split()
+ 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<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
+ if not m:
+ warnings.warn(
+ "Expected glibc version with 2 components major.minor,"
+ " got: %s" % version_str,
+ RuntimeWarning,
+ )
+ return -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(name: str, 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 # noqa
+ 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(linux: str, arch: str) -> Iterator[str]:
+ if not _have_compatible_abi(arch):
+ return
+ # Oldest glibc to be supported regardless of architecture is (2, 17).
+ too_old_glibc2 = _GLibCVersion(2, 16)
+ if arch in {"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 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(tag, arch, glibc_version):
+ yield linux.replace("linux", tag)
+ # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags.
+ if glibc_version in _LEGACY_MANYLINUX_MAP:
+ legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version]
+ if _is_compatible(legacy_tag, arch, glibc_version):
+ yield linux.replace("linux", legacy_tag)
diff --git a/setuptools/_vendor/packaging/_musllinux.py b/setuptools/_vendor/packaging/_musllinux.py
new file mode 100644
index 00000000..85450faf
--- /dev/null
+++ b/setuptools/_vendor/packaging/_musllinux.py
@@ -0,0 +1,136 @@
+"""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 contextlib
+import functools
+import operator
+import os
+import re
+import struct
+import subprocess
+import sys
+from typing import IO, Iterator, NamedTuple, Optional, Tuple
+
+
+def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]:
+ return struct.unpack(fmt, f.read(struct.calcsize(fmt)))
+
+
+def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]:
+ """Detect musl libc location by parsing the Python executable.
+
+ Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca
+ ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
+ """
+ f.seek(0)
+ try:
+ ident = _read_unpacked(f, "16B")
+ except struct.error:
+ return None
+ if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF.
+ return None
+ f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version.
+
+ 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, p_fmt, p_idx = {
+ 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit.
+ 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit.
+ }[ident[4]]
+ except KeyError:
+ return None
+ else:
+ p_get = operator.itemgetter(*p_idx)
+
+ # Find the interpreter section and return its content.
+ try:
+ _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt)
+ except struct.error:
+ return None
+ for i in range(e_phnum + 1):
+ f.seek(e_phoff + e_phentsize * i)
+ try:
+ p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt))
+ except struct.error:
+ return None
+ if p_type != 3: # Not PT_INTERP.
+ continue
+ f.seek(p_offset)
+ interpreter = os.fsdecode(f.read(p_filesz)).strip("\0")
+ if "musl" not in interpreter:
+ return None
+ return interpreter
+ return None
+
+
+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
+ """
+ with contextlib.ExitStack() as stack:
+ try:
+ f = stack.enter_context(open(executable, "rb"))
+ except IOError:
+ return None
+ ld = _parse_ld_musl_from_elf(f)
+ if not ld:
+ return None
+ proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True)
+ return _parse_musl_version(proc.stderr)
+
+
+def platform_tags(arch: str) -> Iterator[str]:
+ """Generate musllinux tags compatible to the current platform.
+
+ :param arch: Should 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 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/setuptools/_vendor/packaging/_structures.py b/setuptools/_vendor/packaging/_structures.py
index 800d5c55..95154975 100644
--- a/setuptools/_vendor/packaging/_structures.py
+++ b/setuptools/_vendor/packaging/_structures.py
@@ -1,85 +1,66 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
-class InfinityType(object):
- def __repr__(self):
- # type: () -> str
+class InfinityType:
+ def __repr__(self) -> str:
return "Infinity"
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(repr(self))
- def __lt__(self, other):
- # type: (object) -> bool
+ def __lt__(self, other: object) -> bool:
return False
- def __le__(self, other):
- # type: (object) -> bool
+ def __le__(self, other: object) -> bool:
return False
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
return isinstance(other, self.__class__)
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
return not isinstance(other, self.__class__)
- def __gt__(self, other):
- # type: (object) -> bool
+ def __gt__(self, other: object) -> bool:
return True
- def __ge__(self, other):
- # type: (object) -> bool
+ def __ge__(self, other: object) -> bool:
return True
- def __neg__(self):
- # type: (object) -> NegativeInfinityType
+ def __neg__(self: object) -> "NegativeInfinityType":
return NegativeInfinity
Infinity = InfinityType()
-class NegativeInfinityType(object):
- def __repr__(self):
- # type: () -> str
+class NegativeInfinityType:
+ def __repr__(self) -> str:
return "-Infinity"
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(repr(self))
- def __lt__(self, other):
- # type: (object) -> bool
+ def __lt__(self, other: object) -> bool:
return True
- def __le__(self, other):
- # type: (object) -> bool
+ def __le__(self, other: object) -> bool:
return True
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
return isinstance(other, self.__class__)
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
return not isinstance(other, self.__class__)
- def __gt__(self, other):
- # type: (object) -> bool
+ def __gt__(self, other: object) -> bool:
return False
- def __ge__(self, other):
- # type: (object) -> bool
+ def __ge__(self, other: object) -> bool:
return False
- def __neg__(self):
- # type: (object) -> InfinityType
+ def __neg__(self: object) -> InfinityType:
return Infinity
diff --git a/setuptools/_vendor/packaging/_typing.py b/setuptools/_vendor/packaging/_typing.py
deleted file mode 100644
index 77a8b918..00000000
--- a/setuptools/_vendor/packaging/_typing.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""For neatly implementing static typing in packaging.
-
-`mypy` - the static type analysis tool we use - uses the `typing` module, which
-provides core functionality fundamental to mypy's functioning.
-
-Generally, `typing` would be imported at runtime and used in that fashion -
-it acts as a no-op at runtime and does not have any run-time overhead by
-design.
-
-As it turns out, `typing` is not vendorable - it uses separate sources for
-Python 2/Python 3. Thus, this codebase can not expect it to be present.
-To work around this, mypy allows the typing import to be behind a False-y
-optional to prevent it from running at runtime and type-comments can be used
-to remove the need for the types to be accessible directly during runtime.
-
-This module provides the False-y guard in a nicely named fashion so that a
-curious maintainer can reach here to read this.
-
-In packaging, all static-typing related imports should be guarded as follows:
-
- from packaging._typing import TYPE_CHECKING
-
- if TYPE_CHECKING:
- from typing import ...
-
-Ref: https://github.com/python/mypy/issues/3216
-"""
-
-__all__ = ["TYPE_CHECKING", "cast"]
-
-# The TYPE_CHECKING constant defined by the typing module is False at runtime
-# but True while type checking.
-if False: # pragma: no cover
- from typing import TYPE_CHECKING
-else:
- TYPE_CHECKING = False
-
-# typing's cast syntax requires calling typing.cast at runtime, but we don't
-# want to import typing at runtime. Here, we inform the type checkers that
-# we're importing `typing.cast` as `cast` and re-implement typing.cast's
-# runtime behavior in a block that is ignored by type checkers.
-if TYPE_CHECKING: # pragma: no cover
- # not executed at runtime
- from typing import cast
-else:
- # executed at runtime
- def cast(type_, value): # noqa
- return value
diff --git a/setuptools/_vendor/packaging/markers.py b/setuptools/_vendor/packaging/markers.py
index 03fbdfcc..eb0541b8 100644
--- a/setuptools/_vendor/packaging/markers.py
+++ b/setuptools/_vendor/packaging/markers.py
@@ -1,26 +1,26 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import operator
import os
import platform
import sys
+from typing import Any, Callable, Dict, List, Optional, Tuple, Union
+
+from setuptools.extern.pyparsing import ( # noqa: N817
+ Forward,
+ Group,
+ Literal as L,
+ ParseException,
+ ParseResults,
+ QuotedString,
+ ZeroOrMore,
+ stringEnd,
+ stringStart,
+)
-from setuptools.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd
-from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString
-from setuptools.extern.pyparsing import Literal as L # noqa
-
-from ._compat import string_types
-from ._typing import TYPE_CHECKING
-from .specifiers import Specifier, InvalidSpecifier
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
-
- Operator = Callable[[str, str], bool]
-
+from .specifiers import InvalidSpecifier, Specifier
__all__ = [
"InvalidMarker",
@@ -30,6 +30,8 @@ __all__ = [
"default_environment",
]
+Operator = Callable[[str, str], bool]
+
class InvalidMarker(ValueError):
"""
@@ -50,39 +52,32 @@ class UndefinedEnvironmentName(ValueError):
"""
-class Node(object):
- def __init__(self, value):
- # type: (Any) -> None
+class Node:
+ def __init__(self, value: Any) -> None:
self.value = value
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return str(self.value)
- def __repr__(self):
- # type: () -> str
- return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
+ def __repr__(self) -> str:
+ return f"<{self.__class__.__name__}('{self}')>"
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
raise NotImplementedError
class Variable(Node):
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
return str(self)
class Value(Node):
- def serialize(self):
- # type: () -> str
- return '"{0}"'.format(self)
+ def serialize(self) -> str:
+ return f'"{self}"'
class Op(Node):
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
return str(self)
@@ -143,18 +138,18 @@ MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR)
MARKER = stringStart + MARKER_EXPR + stringEnd
-def _coerce_parse_result(results):
- # type: (Union[ParseResults, List[Any]]) -> List[Any]
+def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]:
if isinstance(results, ParseResults):
return [_coerce_parse_result(i) for i in results]
else:
return results
-def _format_marker(marker, first=True):
- # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
+def _format_marker(
+ marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True
+) -> str:
- assert isinstance(marker, (list, tuple, string_types))
+ 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
@@ -179,7 +174,7 @@ def _format_marker(marker, first=True):
return marker
-_operators = {
+_operators: Dict[str, Operator] = {
"in": lambda lhs, rhs: lhs in rhs,
"not in": lambda lhs, rhs: lhs not in rhs,
"<": operator.lt,
@@ -188,11 +183,10 @@ _operators = {
"!=": operator.ne,
">=": operator.ge,
">": operator.gt,
-} # type: Dict[str, Operator]
+}
-def _eval_op(lhs, op, rhs):
- # type: (str, Op, str) -> bool
+def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
try:
spec = Specifier("".join([op.serialize(), rhs]))
except InvalidSpecifier:
@@ -200,40 +194,36 @@ def _eval_op(lhs, op, rhs):
else:
return spec.contains(lhs)
- oper = _operators.get(op.serialize()) # type: Optional[Operator]
+ oper: Optional[Operator] = _operators.get(op.serialize())
if oper is None:
- raise UndefinedComparison(
- "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
- )
+ raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
return oper(lhs, rhs)
-class Undefined(object):
+class Undefined:
pass
_undefined = Undefined()
-def _get_env(environment, name):
- # type: (Dict[str, str], str) -> str
- value = environment.get(name, _undefined) # type: Union[str, Undefined]
+def _get_env(environment: Dict[str, str], name: str) -> str:
+ value: Union[str, Undefined] = environment.get(name, _undefined)
if isinstance(value, Undefined):
raise UndefinedEnvironmentName(
- "{0!r} does not exist in evaluation environment.".format(name)
+ f"{name!r} does not exist in evaluation environment."
)
return value
-def _evaluate_markers(markers, environment):
- # type: (List[Any], Dict[str, str]) -> bool
- groups = [[]] # type: List[List[bool]]
+def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool:
+ groups: List[List[bool]] = [[]]
for marker in markers:
- assert isinstance(marker, (list, tuple, string_types))
+ assert isinstance(marker, (list, tuple, str))
if isinstance(marker, list):
groups[-1].append(_evaluate_markers(marker, environment))
@@ -256,8 +246,7 @@ def _evaluate_markers(markers, environment):
return any(all(item) for item in groups)
-def format_full_version(info):
- # type: (sys._version_info) -> str
+def format_full_version(info: "sys._version_info") -> str:
version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel
if kind != "final":
@@ -265,18 +254,9 @@ def format_full_version(info):
return version
-def default_environment():
- # type: () -> Dict[str, str]
- if hasattr(sys, "implementation"):
- # Ignoring the `sys.implementation` reference for type checking due to
- # mypy not liking that the attribute doesn't exist in Python 2.7 when
- # run with the `--py27` flag.
- iver = format_full_version(sys.implementation.version) # type: ignore
- implementation_name = sys.implementation.name # type: ignore
- else:
- iver = "0"
- implementation_name = ""
-
+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,
@@ -292,27 +272,23 @@ def default_environment():
}
-class Marker(object):
- def __init__(self, marker):
- # type: (str) -> None
+class Marker:
+ def __init__(self, marker: str) -> None:
try:
self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e:
- err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
- marker, marker[e.loc : e.loc + 8]
+ raise InvalidMarker(
+ f"Invalid marker: {marker!r}, parse error at "
+ f"{marker[e.loc : e.loc + 8]!r}"
)
- raise InvalidMarker(err_str)
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return _format_marker(self._markers)
- def __repr__(self):
- # type: () -> str
- return "<Marker({0!r})>".format(str(self))
+ def __repr__(self) -> str:
+ return f"<Marker('{self}')>"
- def evaluate(self, environment=None):
- # type: (Optional[Dict[str, str]]) -> bool
+ def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
"""Evaluate a marker.
Return the boolean from evaluating the given marker against the
diff --git a/setuptools/_vendor/packaging/requirements.py b/setuptools/_vendor/packaging/requirements.py
index 5d50c7d7..0d93231b 100644
--- a/setuptools/_vendor/packaging/requirements.py
+++ b/setuptools/_vendor/packaging/requirements.py
@@ -1,23 +1,28 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
-import string
import re
+import string
+import urllib.parse
+from typing import List, Optional as TOptional, Set
+
+from setuptools.extern.pyparsing import ( # noqa
+ Combine,
+ Literal as L,
+ Optional,
+ ParseException,
+ Regex,
+ Word,
+ ZeroOrMore,
+ originalTextFor,
+ stringEnd,
+ stringStart,
+)
-from setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException
-from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine
-from setuptools.extern.pyparsing import Literal as L # noqa
-from urllib import parse as urlparse
-
-from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
-if TYPE_CHECKING: # pragma: no cover
- from typing import List
-
class InvalidRequirement(ValueError):
"""
@@ -55,7 +60,7 @@ VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
VERSION_MANY = Combine(
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
)("_raw_spec")
-_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
+_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
@@ -79,7 +84,7 @@ REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
REQUIREMENT.parseString("x[]")
-class Requirement(object):
+class Requirement:
"""Parse a requirement.
Parse a given requirement string into its parts, such as name, specifier,
@@ -92,54 +97,50 @@ class Requirement(object):
# the thing as well as the version? What about the markers?
# TODO: Can we normalize the name and extra name?
- def __init__(self, requirement_string):
- # type: (str) -> None
+ def __init__(self, requirement_string: str) -> None:
try:
req = REQUIREMENT.parseString(requirement_string)
except ParseException as e:
raise InvalidRequirement(
- 'Parse error at "{0!r}": {1}'.format(
- requirement_string[e.loc : e.loc + 8], e.msg
- )
+ f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}'
)
- self.name = req.name
+ self.name: str = req.name
if req.url:
- parsed_url = urlparse.urlparse(req.url)
+ parsed_url = urllib.parse.urlparse(req.url)
if parsed_url.scheme == "file":
- if urlparse.urlunparse(parsed_url) != req.url:
+ if urllib.parse.urlunparse(parsed_url) != req.url:
raise InvalidRequirement("Invalid URL given")
elif not (parsed_url.scheme and parsed_url.netloc) or (
not parsed_url.scheme and not parsed_url.netloc
):
- raise InvalidRequirement("Invalid URL: {0}".format(req.url))
- self.url = req.url
+ raise InvalidRequirement(f"Invalid URL: {req.url}")
+ self.url: TOptional[str] = req.url
else:
self.url = None
- self.extras = set(req.extras.asList() if req.extras else [])
- self.specifier = SpecifierSet(req.specifier)
- self.marker = req.marker if req.marker else None
+ self.extras: Set[str] = set(req.extras.asList() if req.extras else [])
+ self.specifier: SpecifierSet = SpecifierSet(req.specifier)
+ self.marker: TOptional[Marker] = req.marker if req.marker else None
- def __str__(self):
- # type: () -> str
- parts = [self.name] # type: List[str]
+ def __str__(self) -> str:
+ parts: List[str] = [self.name]
if self.extras:
- parts.append("[{0}]".format(",".join(sorted(self.extras))))
+ formatted_extras = ",".join(sorted(self.extras))
+ parts.append(f"[{formatted_extras}]")
if self.specifier:
parts.append(str(self.specifier))
if self.url:
- parts.append("@ {0}".format(self.url))
+ parts.append(f"@ {self.url}")
if self.marker:
parts.append(" ")
if self.marker:
- parts.append("; {0}".format(self.marker))
+ parts.append(f"; {self.marker}")
return "".join(parts)
- def __repr__(self):
- # type: () -> str
- return "<Requirement({0!r})>".format(str(self))
+ def __repr__(self) -> str:
+ return f"<Requirement('{self}')>"
diff --git a/setuptools/_vendor/packaging/specifiers.py b/setuptools/_vendor/packaging/specifiers.py
index fe09bb1d..ce66bd4a 100644
--- a/setuptools/_vendor/packaging/specifiers.py
+++ b/setuptools/_vendor/packaging/specifiers.py
@@ -1,34 +1,33 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import abc
import functools
import itertools
import re
+import warnings
+from typing import (
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Pattern,
+ Set,
+ Tuple,
+ TypeVar,
+ Union,
+)
-from ._compat import string_types, with_metaclass
-from ._typing import TYPE_CHECKING
from .utils import canonicalize_version
-from .version import Version, LegacyVersion, parse
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import (
- List,
- Dict,
- Union,
- Iterable,
- Iterator,
- Optional,
- Callable,
- Tuple,
- FrozenSet,
- )
+from .version import LegacyVersion, Version, parse
- ParsedVersion = Union[Version, LegacyVersion]
- UnparsedVersion = Union[Version, LegacyVersion, str]
- CallableOperator = Callable[[ParsedVersion, str], bool]
+ParsedVersion = Union[Version, LegacyVersion]
+UnparsedVersion = Union[Version, LegacyVersion, str]
+VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion)
+CallableOperator = Callable[[ParsedVersion, str], bool]
class InvalidSpecifier(ValueError):
@@ -37,64 +36,58 @@ class InvalidSpecifier(ValueError):
"""
-class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore
+class BaseSpecifier(metaclass=abc.ABCMeta):
@abc.abstractmethod
- def __str__(self):
- # type: () -> str
+ 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):
- # type: () -> int
+ def __hash__(self) -> int:
"""
Returns a hash value for this Specifier like object.
"""
@abc.abstractmethod
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
"""
Returns a boolean representing whether or not the two Specifier like
objects are equal.
"""
@abc.abstractmethod
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
"""
Returns a boolean representing whether or not the two Specifier like
objects are not equal.
"""
@abc.abstractproperty
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
"""
Returns whether or not pre-releases as a whole are allowed by this
specifier.
"""
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
"""
Sets whether or not pre-releases as a whole are allowed by this
specifier.
"""
@abc.abstractmethod
- def contains(self, item, prereleases=None):
- # type: (str, Optional[bool]) -> bool
+ 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, prereleases=None):
- # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
+ def filter(
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
"""
Takes an iterable of items and filters them so that only items which
are contained within this specifier are allowed in it.
@@ -103,48 +96,43 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore
class _IndividualSpecifier(BaseSpecifier):
- _operators = {} # type: Dict[str, str]
+ _operators: Dict[str, str] = {}
+ _regex: Pattern[str]
- def __init__(self, spec="", prereleases=None):
- # type: (str, Optional[bool]) -> None
+ def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
match = self._regex.search(spec)
if not match:
- raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
+ raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
- self._spec = (
+ self._spec: Tuple[str, str] = (
match.group("operator").strip(),
match.group("version").strip(),
- ) # type: Tuple[str, str]
+ )
# Store whether or not this Specifier should accept prereleases
self._prereleases = prereleases
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
pre = (
- ", prereleases={0!r}".format(self.prereleases)
+ f", prereleases={self.prereleases!r}"
if self._prereleases is not None
else ""
)
- return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
+ return "<{}({!r}{})>".format(self.__class__.__name__, str(self), pre)
- def __str__(self):
- # type: () -> str
- return "{0}{1}".format(*self._spec)
+ def __str__(self) -> str:
+ return "{}{}".format(*self._spec)
@property
- def _canonical_spec(self):
- # type: () -> Tuple[str, Union[Version, str]]
+ def _canonical_spec(self) -> Tuple[str, str]:
return self._spec[0], canonicalize_version(self._spec[1])
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._canonical_spec)
- def __eq__(self, other):
- # type: (object) -> bool
- if isinstance(other, string_types):
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, str):
try:
other = self.__class__(str(other))
except InvalidSpecifier:
@@ -154,9 +142,8 @@ class _IndividualSpecifier(BaseSpecifier):
return self._canonical_spec == other._canonical_spec
- def __ne__(self, other):
- # type: (object) -> bool
- if isinstance(other, string_types):
+ def __ne__(self, other: object) -> bool:
+ if isinstance(other, str):
try:
other = self.__class__(str(other))
except InvalidSpecifier:
@@ -166,45 +153,39 @@ class _IndividualSpecifier(BaseSpecifier):
return self._spec != other._spec
- def _get_operator(self, op):
- # type: (str) -> CallableOperator
- operator_callable = getattr(
- self, "_compare_{0}".format(self._operators[op])
- ) # type: CallableOperator
+ def _get_operator(self, op: str) -> CallableOperator:
+ operator_callable: CallableOperator = getattr(
+ self, f"_compare_{self._operators[op]}"
+ )
return operator_callable
- def _coerce_version(self, version):
- # type: (UnparsedVersion) -> ParsedVersion
+ def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion:
if not isinstance(version, (LegacyVersion, Version)):
version = parse(version)
return version
@property
- def operator(self):
- # type: () -> str
+ def operator(self) -> str:
return self._spec[0]
@property
- def version(self):
- # type: () -> str
+ def version(self) -> str:
return self._spec[1]
@property
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
return self._prereleases
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
- def __contains__(self, item):
- # type: (str) -> bool
+ def __contains__(self, item: str) -> bool:
return self.contains(item)
- def contains(self, item, prereleases=None):
- # type: (UnparsedVersion, Optional[bool]) -> bool
+ def contains(
+ self, item: UnparsedVersion, prereleases: Optional[bool] = None
+ ) -> bool:
# Determine if prereleases are to be allowed or not.
if prereleases is None:
@@ -222,11 +203,12 @@ class _IndividualSpecifier(BaseSpecifier):
# Actually do the comparison to determine if this item is contained
# within this Specifier or not.
- operator_callable = self._get_operator(self.operator) # type: CallableOperator
+ operator_callable: CallableOperator = self._get_operator(self.operator)
return operator_callable(normalized_item, self.version)
- def filter(self, iterable, prereleases=None):
- # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
+ def filter(
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
yielded = False
found_prereleases = []
@@ -240,7 +222,7 @@ class _IndividualSpecifier(BaseSpecifier):
if self.contains(parsed_version, **kw):
# If our version is a prerelease, and we were not set to allow
- # prereleases, then we'll store it for later incase nothing
+ # 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
@@ -285,44 +267,46 @@ class LegacySpecifier(_IndividualSpecifier):
">": "greater_than",
}
- def _coerce_version(self, version):
- # type: (Union[ParsedVersion, str]) -> LegacyVersion
+ def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
+ super().__init__(spec, prereleases)
+
+ warnings.warn(
+ "Creating a LegacyVersion has been deprecated and will be "
+ "removed in the next major release",
+ DeprecationWarning,
+ )
+
+ def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion:
if not isinstance(version, LegacyVersion):
version = LegacyVersion(str(version))
return version
- def _compare_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective == self._coerce_version(spec)
- def _compare_not_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective != self._coerce_version(spec)
- def _compare_less_than_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective <= self._coerce_version(spec)
- def _compare_greater_than_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_greater_than_equal(
+ self, prospective: LegacyVersion, spec: str
+ ) -> bool:
return prospective >= self._coerce_version(spec)
- def _compare_less_than(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective < self._coerce_version(spec)
- def _compare_greater_than(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective > self._coerce_version(spec)
def _require_version_compare(
- fn # type: (Callable[[Specifier, ParsedVersion, str], bool])
-):
- # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool]
+ fn: Callable[["Specifier", ParsedVersion, str], bool]
+) -> Callable[["Specifier", ParsedVersion, str], bool]:
@functools.wraps(fn)
- def wrapped(self, prospective, spec):
- # type: (Specifier, ParsedVersion, str) -> bool
+ def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool:
if not isinstance(prospective, Version):
return False
return fn(self, prospective, spec)
@@ -439,8 +423,7 @@ class Specifier(_IndividualSpecifier):
}
@_require_version_compare
- def _compare_compatible(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_compatible(self, prospective: ParsedVersion, 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
@@ -449,15 +432,9 @@ class Specifier(_IndividualSpecifier):
# the other specifiers.
# We want everything but the last item in the version, but we want to
- # ignore post and dev releases and we want to treat the pre-release as
- # it's own separate segment.
+ # ignore suffix segments.
prefix = ".".join(
- list(
- itertools.takewhile(
- lambda x: (not x.startswith("post") and not x.startswith("dev")),
- _version_split(spec),
- )
- )[:-1]
+ list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
)
# Add the prefix notation to the end of our string
@@ -468,8 +445,7 @@ class Specifier(_IndividualSpecifier):
)
@_require_version_compare
- def _compare_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool:
# We need special logic to handle prefix matching
if spec.endswith(".*"):
@@ -509,13 +485,11 @@ class Specifier(_IndividualSpecifier):
return prospective == spec_version
@_require_version_compare
- def _compare_not_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool:
return not self._compare_equal(prospective, spec)
@_require_version_compare
- def _compare_less_than_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
@@ -523,8 +497,9 @@ class Specifier(_IndividualSpecifier):
return Version(prospective.public) <= Version(spec)
@_require_version_compare
- def _compare_greater_than_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_greater_than_equal(
+ self, prospective: ParsedVersion, spec: str
+ ) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
@@ -532,8 +507,7 @@ class Specifier(_IndividualSpecifier):
return Version(prospective.public) >= Version(spec)
@_require_version_compare
- def _compare_less_than(self, prospective, spec_str):
- # type: (ParsedVersion, str) -> bool
+ def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool:
# Convert our spec to a Version instance, since we'll want to work with
# it as a version.
@@ -559,8 +533,7 @@ class Specifier(_IndividualSpecifier):
return True
@_require_version_compare
- def _compare_greater_than(self, prospective, spec_str):
- # type: (ParsedVersion, str) -> bool
+ def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool:
# Convert our spec to a Version instance, since we'll want to work with
# it as a version.
@@ -591,13 +564,11 @@ class Specifier(_IndividualSpecifier):
# same version in the spec.
return True
- def _compare_arbitrary(self, prospective, spec):
- # type: (Version, str) -> bool
+ def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
return str(prospective).lower() == str(spec).lower()
@property
- def prereleases(self):
- # type: () -> bool
+ def prereleases(self) -> bool:
# If there is an explicit prereleases set for this, then we'll just
# blindly use that.
@@ -622,17 +593,15 @@ class Specifier(_IndividualSpecifier):
return False
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
-def _version_split(version):
- # type: (str) -> List[str]
- result = [] # type: List[str]
+def _version_split(version: str) -> List[str]:
+ result: List[str] = []
for item in version.split("."):
match = _prefix_regex.search(item)
if match:
@@ -642,8 +611,13 @@ def _version_split(version):
return result
-def _pad_version(left, right):
- # type: (List[str], List[str]) -> Tuple[List[str], List[str]]
+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
@@ -662,8 +636,9 @@ def _pad_version(left, right):
class SpecifierSet(BaseSpecifier):
- def __init__(self, specifiers="", prereleases=None):
- # type: (str, Optional[bool]) -> None
+ def __init__(
+ self, specifiers: str = "", prereleases: Optional[bool] = None
+ ) -> None:
# Split on , to break each individual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace.
@@ -671,7 +646,7 @@ class SpecifierSet(BaseSpecifier):
# Parsed each individual specifier, attempting first to make it a
# Specifier and falling back to a LegacySpecifier.
- parsed = set()
+ parsed: Set[_IndividualSpecifier] = set()
for specifier in split_specifiers:
try:
parsed.add(Specifier(specifier))
@@ -685,27 +660,23 @@ class SpecifierSet(BaseSpecifier):
# we accept prereleases or not.
self._prereleases = prereleases
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
pre = (
- ", prereleases={0!r}".format(self.prereleases)
+ f", prereleases={self.prereleases!r}"
if self._prereleases is not None
else ""
)
- return "<SpecifierSet({0!r}{1})>".format(str(self), pre)
+ return "<SpecifierSet({!r}{})>".format(str(self), pre)
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return ",".join(sorted(str(s) for s in self._specs))
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._specs)
- def __and__(self, other):
- # type: (Union[SpecifierSet, str]) -> SpecifierSet
- if isinstance(other, string_types):
+ def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":
+ if isinstance(other, str):
other = SpecifierSet(other)
elif not isinstance(other, SpecifierSet):
return NotImplemented
@@ -727,35 +698,30 @@ class SpecifierSet(BaseSpecifier):
return specifier
- def __eq__(self, other):
- # type: (object) -> bool
- if isinstance(other, (string_types, _IndividualSpecifier)):
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, (str, _IndividualSpecifier)):
other = SpecifierSet(str(other))
elif not isinstance(other, SpecifierSet):
return NotImplemented
return self._specs == other._specs
- def __ne__(self, other):
- # type: (object) -> bool
- if isinstance(other, (string_types, _IndividualSpecifier)):
+ def __ne__(self, other: object) -> bool:
+ if isinstance(other, (str, _IndividualSpecifier)):
other = SpecifierSet(str(other))
elif not isinstance(other, SpecifierSet):
return NotImplemented
return self._specs != other._specs
- def __len__(self):
- # type: () -> int
+ def __len__(self) -> int:
return len(self._specs)
- def __iter__(self):
- # type: () -> Iterator[FrozenSet[_IndividualSpecifier]]
+ def __iter__(self) -> Iterator[_IndividualSpecifier]:
return iter(self._specs)
@property
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
# If we have been given an explicit prerelease modifier, then we'll
# pass that through here.
@@ -773,16 +739,15 @@ class SpecifierSet(BaseSpecifier):
return any(s.prereleases for s in self._specs)
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
- def __contains__(self, item):
- # type: (Union[ParsedVersion, str]) -> bool
+ def __contains__(self, item: UnparsedVersion) -> bool:
return self.contains(item)
- def contains(self, item, prereleases=None):
- # type: (Union[ParsedVersion, str], Optional[bool]) -> bool
+ def contains(
+ self, item: UnparsedVersion, prereleases: Optional[bool] = None
+ ) -> bool:
# Ensure that our item is a Version or LegacyVersion instance.
if not isinstance(item, (LegacyVersion, Version)):
@@ -810,11 +775,8 @@ class SpecifierSet(BaseSpecifier):
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
def filter(
- self,
- iterable, # type: Iterable[Union[ParsedVersion, str]]
- prereleases=None, # type: Optional[bool]
- ):
- # type: (...) -> Iterable[Union[ParsedVersion, str]]
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
# 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
@@ -833,8 +795,11 @@ class SpecifierSet(BaseSpecifier):
# which will filter out any pre-releases, unless there are no final
# releases, and which will filter out LegacyVersion in general.
else:
- filtered = [] # type: List[Union[ParsedVersion, str]]
- found_prereleases = [] # type: List[Union[ParsedVersion, str]]
+ filtered: List[VersionTypeVar] = []
+ found_prereleases: List[VersionTypeVar] = []
+
+ item: UnparsedVersion
+ parsed_version: Union[Version, LegacyVersion]
for item in iterable:
# Ensure that we some kind of Version class for this item.
diff --git a/setuptools/_vendor/packaging/tags.py b/setuptools/_vendor/packaging/tags.py
index 9064910b..e65890a9 100644
--- a/setuptools/_vendor/packaging/tags.py
+++ b/setuptools/_vendor/packaging/tags.py
@@ -2,62 +2,44 @@
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import
-
-import distutils.util
-
-try:
- from importlib.machinery import EXTENSION_SUFFIXES
-except ImportError: # pragma: no cover
- import imp
-
- EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()]
- del imp
import logging
-import os
import platform
-import re
-import struct
import sys
import sysconfig
-import warnings
-
-from ._typing import TYPE_CHECKING, cast
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import (
- Dict,
- FrozenSet,
- IO,
- Iterable,
- Iterator,
- List,
- Optional,
- Sequence,
- Tuple,
- Union,
- )
-
- PythonVersion = Sequence[int]
- MacVersion = Tuple[int, int]
- GlibcVersion = Tuple[int, int]
-
+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__)
-INTERPRETER_SHORT_NAMES = {
+PythonVersion = Sequence[int]
+MacVersion = Tuple[int, int]
+
+INTERPRETER_SHORT_NAMES: Dict[str, str] = {
"python": "py", # Generic.
"cpython": "cp",
"pypy": "pp",
"ironpython": "ip",
"jython": "jy",
-} # type: Dict[str, str]
+}
_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
-class Tag(object):
+class Tag:
"""
A representation of the tag triple for a wheel.
@@ -65,55 +47,53 @@ class Tag(object):
is also supported.
"""
- __slots__ = ["_interpreter", "_abi", "_platform"]
+ __slots__ = ["_interpreter", "_abi", "_platform", "_hash"]
- def __init__(self, interpreter, abi, platform):
- # type: (str, str, str) -> None
+ 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):
- # type: () -> str
+ def interpreter(self) -> str:
return self._interpreter
@property
- def abi(self):
- # type: () -> str
+ def abi(self) -> str:
return self._abi
@property
- def platform(self):
- # type: () -> str
+ def platform(self) -> str:
return self._platform
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
if not isinstance(other, Tag):
return NotImplemented
return (
- (self.platform == other.platform)
- and (self.abi == other.abi)
- and (self.interpreter == other.interpreter)
+ (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):
- # type: () -> int
- return hash((self._interpreter, self._abi, self._platform))
+ def __hash__(self) -> int:
+ return self._hash
- def __str__(self):
- # type: () -> str
- return "{}-{}-{}".format(self._interpreter, self._abi, self._platform)
+ def __str__(self) -> str:
+ return f"{self._interpreter}-{self._abi}-{self._platform}"
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
return "<{self} @ {self_id}>".format(self=self, self_id=id(self))
-def parse_tag(tag):
- # type: (str) -> FrozenSet[Tag]
+def parse_tag(tag: str) -> FrozenSet[Tag]:
"""
Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
@@ -129,24 +109,7 @@ def parse_tag(tag):
return frozenset(tags)
-def _warn_keyword_parameter(func_name, kwargs):
- # type: (str, Dict[str, bool]) -> bool
- """
- Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only.
- """
- if not kwargs:
- return False
- elif len(kwargs) > 1 or "warn" not in kwargs:
- kwargs.pop("warn", None)
- arg = next(iter(kwargs.keys()))
- raise TypeError(
- "{}() got an unexpected keyword argument {!r}".format(func_name, arg)
- )
- return kwargs["warn"]
-
-
-def _get_config_var(name, warn=False):
- # type: (str, bool) -> Union[int, str, None]
+def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]:
value = sysconfig.get_config_var(name)
if value is None and warn:
logger.debug(
@@ -155,13 +118,11 @@ def _get_config_var(name, warn=False):
return value
-def _normalize_string(string):
- # type: (str) -> str
+def _normalize_string(string: str) -> str:
return string.replace(".", "_").replace("-", "_")
-def _abi3_applies(python_version):
- # type: (PythonVersion) -> bool
+def _abi3_applies(python_version: PythonVersion) -> bool:
"""
Determine if the Python version supports abi3.
@@ -170,8 +131,7 @@ def _abi3_applies(python_version):
return len(python_version) > 1 and tuple(python_version) >= (3, 2)
-def _cpython_abis(py_version, warn=False):
- # type: (PythonVersion, bool) -> List[str]
+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])
@@ -197,7 +157,7 @@ def _cpython_abis(py_version, warn=False):
elif debug:
# Debug builds can also load "normal" extension modules.
# We can also assume no UCS-4 or pymalloc requirement.
- abis.append("cp{version}".format(version=version))
+ abis.append(f"cp{version}")
abis.insert(
0,
"cp{version}{debug}{pymalloc}{ucs4}".format(
@@ -208,12 +168,12 @@ def _cpython_abis(py_version, warn=False):
def cpython_tags(
- python_version=None, # type: Optional[PythonVersion]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
-):
- # type: (...) -> Iterator[Tag]
+ 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.
@@ -229,7 +189,6 @@ def cpython_tags(
If 'abi3' or 'none' are specified in 'abis' then they will be yielded at
their normal position and not at the beginning.
"""
- warn = _warn_keyword_parameter("cpython_tags", kwargs)
if not python_version:
python_version = sys.version_info[:2]
@@ -248,15 +207,13 @@ def cpython_tags(
except ValueError:
pass
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
for abi in abis:
for platform_ in platforms:
yield Tag(interpreter, abi, platform_)
if _abi3_applies(python_version):
- for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms):
- yield tag
- for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms):
- yield tag
+ yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
+ yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)
if _abi3_applies(python_version):
for minor_version in range(python_version[1] - 1, 1, -1):
@@ -267,20 +224,19 @@ def cpython_tags(
yield Tag(interpreter, "abi3", platform_)
-def _generic_abi():
- # type: () -> Iterator[str]
+def _generic_abi() -> Iterator[str]:
abi = sysconfig.get_config_var("SOABI")
if abi:
yield _normalize_string(abi)
def generic_tags(
- interpreter=None, # type: Optional[str]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
-):
- # type: (...) -> Iterator[Tag]
+ 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.
@@ -289,14 +245,13 @@ def generic_tags(
The "none" ABI will be added if it was not explicitly provided.
"""
- warn = _warn_keyword_parameter("generic_tags", kwargs)
if not interpreter:
interp_name = interpreter_name()
interp_version = interpreter_version(warn=warn)
interpreter = "".join([interp_name, interp_version])
if abis is None:
abis = _generic_abi()
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
abis = list(abis)
if "none" not in abis:
abis.append("none")
@@ -305,8 +260,7 @@ def generic_tags(
yield Tag(interpreter, abi, platform_)
-def _py_interpreter_range(py_version):
- # type: (PythonVersion) -> Iterator[str]
+def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
"""
Yields Python versions in descending order.
@@ -322,11 +276,10 @@ def _py_interpreter_range(py_version):
def compatible_tags(
- python_version=None, # type: Optional[PythonVersion]
- interpreter=None, # type: Optional[str]
- platforms=None, # type: Optional[Iterable[str]]
-):
- # type: (...) -> Iterator[Tag]
+ 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.
@@ -337,7 +290,7 @@ def compatible_tags(
"""
if not python_version:
python_version = sys.version_info[:2]
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
for version in _py_interpreter_range(python_version):
for platform_ in platforms:
yield Tag(version, "none", platform_)
@@ -347,8 +300,7 @@ def compatible_tags(
yield Tag(version, "none", "any")
-def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
- # type: (str, bool) -> str
+def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
if not is_32bit:
return arch
@@ -358,8 +310,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
return "i386"
-def _mac_binary_formats(version, cpu_arch):
- # type: (MacVersion, str) -> List[str]
+def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]:
formats = [cpu_arch]
if cpu_arch == "x86_64":
if version < (10, 4):
@@ -382,12 +333,18 @@ def _mac_binary_formats(version, cpu_arch):
return []
formats.extend(["fat32", "fat"])
- formats.append("universal")
+ 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=None, arch=None):
- # type: (Optional[MacVersion], Optional[str]) -> Iterator[str]
+def mac_platforms(
+ version: Optional[MacVersion] = None, arch: Optional[str] = None
+) -> Iterator[str]:
"""
Yields the platform tags for a macOS system.
@@ -396,7 +353,7 @@ def mac_platforms(version=None, arch=None):
generate platform tags for. Both parameters default to the appropriate value
for the current system.
"""
- version_str, _, cpu_arch = platform.mac_ver() # type: ignore
+ version_str, _, cpu_arch = platform.mac_ver()
if version is None:
version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
else:
@@ -405,283 +362,76 @@ def mac_platforms(version=None, arch=None):
arch = _mac_arch(cpu_arch)
else:
arch = arch
- for minor_version in range(version[1], -1, -1):
- compat_version = version[0], minor_version
- binary_formats = _mac_binary_formats(compat_version, arch)
- for binary_format in binary_formats:
- yield "macosx_{major}_{minor}_{binary_format}".format(
- major=compat_version[0],
- minor=compat_version[1],
- binary_format=binary_format,
- )
-
-
-# From PEP 513.
-def _is_manylinux_compatible(name, glibc_version):
- # type: (str, GlibcVersion) -> bool
- # Check for presence of _manylinux module.
- try:
- import _manylinux # noqa
-
- return bool(getattr(_manylinux, name + "_compatible"))
- except (ImportError, AttributeError):
- # Fall through to heuristic check below.
- pass
- return _have_compatible_glibc(*glibc_version)
-
-
-def _glibc_version_string():
- # type: () -> Optional[str]
- # Returns glibc version string, or None if not using glibc.
- return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
-
-
-def _glibc_version_string_confstr():
- # type: () -> Optional[str]
- """
- Primary implementation of glibc_version_string using os.confstr.
- """
- # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
- # to be broken or missing. This strategy is used in the standard library
- # platform module.
- # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183
- try:
- # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
- version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821
- "CS_GNU_LIBC_VERSION"
- )
- assert version_string is not None
- _, version = version_string.split() # type: Tuple[str, str]
- except (AssertionError, AttributeError, OSError, ValueError):
- # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
- return None
- return version
+ 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
+ )
-def _glibc_version_string_ctypes():
- # type: () -> Optional[str]
- """
- Fallback implementation of glibc_version_string using ctypes.
- """
- try:
- import ctypes
- except ImportError:
- return None
-
- # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
- # manpage says, "If filename is NULL, then the returned handle is for the
- # main program". This way we can let the linker do the work to figure out
- # which libc our process is actually using.
- #
- # Note: typeshed is wrong here so we are ignoring this line.
- process_namespace = ctypes.CDLL(None) # type: ignore
- try:
- gnu_get_libc_version = process_namespace.gnu_get_libc_version
- except AttributeError:
- # Symbol doesn't exist -> therefore, we are not linked to
- # glibc.
- return None
-
- # Call gnu_get_libc_version, which returns a string like "2.5"
- gnu_get_libc_version.restype = ctypes.c_char_p
- version_str = gnu_get_libc_version() # type: str
- # py2 / py3 compatibility:
- if not isinstance(version_str, str):
- version_str = version_str.decode("ascii")
-
- return version_str
-
-
-# Separated out from have_compatible_glibc for easier unit testing.
-def _check_glibc_version(version_str, required_major, minimum_minor):
- # type: (str, int, int) -> bool
- # Parse string and check against requested version.
- #
- # We use a regexp instead of str.split because we want to discard any
- # random junk that might come after the minor version -- this might happen
- # in patched/forked versions of glibc (e.g. Linaro's version of glibc
- # uses version strings like "2.20-2014.11"). See gh-3588.
- m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
- if not m:
- warnings.warn(
- "Expected glibc version with 2 components major.minor,"
- " got: %s" % version_str,
- RuntimeWarning,
- )
- return False
- return (
- int(m.group("major")) == required_major
- and int(m.group("minor")) >= minimum_minor
- )
+ 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 _have_compatible_glibc(required_major, minimum_minor):
- # type: (int, int) -> bool
- version_str = _glibc_version_string()
- if version_str is None:
- return False
- return _check_glibc_version(version_str, required_major, minimum_minor)
-
-
-# Python does not provide platform information at sufficient granularity to
-# identify the architecture of the running executable in some cases, so we
-# determine it dynamically by reading the information from the running
-# process. This only applies on Linux, which uses the ELF format.
-class _ELFFileHeader(object):
- # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
- class _InvalidELFFileHeader(ValueError):
- """
- An invalid ELF file header was found.
- """
-
- ELF_MAGIC_NUMBER = 0x7F454C46
- ELFCLASS32 = 1
- ELFCLASS64 = 2
- ELFDATA2LSB = 1
- ELFDATA2MSB = 2
- EM_386 = 3
- EM_S390 = 22
- EM_ARM = 40
- EM_X86_64 = 62
- EF_ARM_ABIMASK = 0xFF000000
- EF_ARM_ABI_VER5 = 0x05000000
- EF_ARM_ABI_FLOAT_HARD = 0x00000400
-
- def __init__(self, file):
- # type: (IO[bytes]) -> None
- def unpack(fmt):
- # type: (str) -> int
- try:
- (result,) = struct.unpack(
- fmt, file.read(struct.calcsize(fmt))
- ) # type: (int, )
- except struct.error:
- raise _ELFFileHeader._InvalidELFFileHeader()
- return result
-
- self.e_ident_magic = unpack(">I")
- if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_class = unpack("B")
- if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_data = unpack("B")
- if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_version = unpack("B")
- self.e_ident_osabi = unpack("B")
- self.e_ident_abiversion = unpack("B")
- self.e_ident_pad = file.read(7)
- format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
- format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
- format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
- format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
- self.e_type = unpack(format_h)
- self.e_machine = unpack(format_h)
- self.e_version = unpack(format_i)
- self.e_entry = unpack(format_p)
- self.e_phoff = unpack(format_p)
- self.e_shoff = unpack(format_p)
- self.e_flags = unpack(format_i)
- self.e_ehsize = unpack(format_h)
- self.e_phentsize = unpack(format_h)
- self.e_phnum = unpack(format_h)
- self.e_shentsize = unpack(format_h)
- self.e_shnum = unpack(format_h)
- self.e_shstrndx = unpack(format_h)
-
-
-def _get_elf_header():
- # type: () -> Optional[_ELFFileHeader]
- try:
- with open(sys.executable, "rb") as f:
- elf_header = _ELFFileHeader(f)
- except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
- return None
- return elf_header
-
-
-def _is_linux_armhf():
- # type: () -> bool
- # hard-float ABI can be detected from the ELF header of the running
- # process
- # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_ARM
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABIMASK
- ) == elf_header.EF_ARM_ABI_VER5
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD
- ) == elf_header.EF_ARM_ABI_FLOAT_HARD
- return result
-
-
-def _is_linux_i686():
- # type: () -> bool
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_386
- return result
-
-
-def _have_compatible_manylinux_abi(arch):
- # type: (str) -> bool
- if arch == "armv7l":
- return _is_linux_armhf()
- if arch == "i686":
- return _is_linux_i686()
- return True
-
-
-def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
- # type: (bool) -> Iterator[str]
- linux = _normalize_string(distutils.util.get_platform())
+def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
+ linux = _normalize_string(sysconfig.get_platform())
if is_32bit:
if linux == "linux_x86_64":
linux = "linux_i686"
elif linux == "linux_aarch64":
linux = "linux_armv7l"
- manylinux_support = []
_, arch = linux.split("_", 1)
- if _have_compatible_manylinux_abi(arch):
- if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}:
- manylinux_support.append(
- ("manylinux2014", (2, 17))
- ) # CentOS 7 w/ glibc 2.17 (PEP 599)
- if arch in {"x86_64", "i686"}:
- manylinux_support.append(
- ("manylinux2010", (2, 12))
- ) # CentOS 6 w/ glibc 2.12 (PEP 571)
- manylinux_support.append(
- ("manylinux1", (2, 5))
- ) # CentOS 5 w/ glibc 2.5 (PEP 513)
- manylinux_support_iter = iter(manylinux_support)
- for name, glibc_version in manylinux_support_iter:
- if _is_manylinux_compatible(name, glibc_version):
- yield linux.replace("linux", name)
- break
- # Support for a later manylinux implies support for an earlier version.
- for name, _ in manylinux_support_iter:
- yield linux.replace("linux", name)
+ yield from _manylinux.platform_tags(linux, arch)
+ yield from _musllinux.platform_tags(arch)
yield linux
-def _generic_platforms():
- # type: () -> Iterator[str]
- yield _normalize_string(distutils.util.get_platform())
+def _generic_platforms() -> Iterator[str]:
+ yield _normalize_string(sysconfig.get_platform())
-def _platform_tags():
- # type: () -> Iterator[str]
+def platform_tags() -> Iterator[str]:
"""
Provides the platform tags for this installation.
"""
@@ -693,25 +443,18 @@ def _platform_tags():
return _generic_platforms()
-def interpreter_name():
- # type: () -> str
+def interpreter_name() -> str:
"""
Returns the name of the running interpreter.
"""
- try:
- name = sys.implementation.name # type: ignore
- except AttributeError: # pragma: no cover
- # Python 2.7 compatibility.
- name = platform.python_implementation().lower()
+ name = sys.implementation.name
return INTERPRETER_SHORT_NAMES.get(name) or name
-def interpreter_version(**kwargs):
- # type: (bool) -> str
+def interpreter_version(*, warn: bool = False) -> str:
"""
Returns the version of the running interpreter.
"""
- warn = _warn_keyword_parameter("interpreter_version", kwargs)
version = _get_config_var("py_version_nodot", warn=warn)
if version:
version = str(version)
@@ -720,32 +463,22 @@ def interpreter_version(**kwargs):
return version
-def _version_nodot(version):
- # type: (PythonVersion) -> str
- if any(v >= 10 for v in version):
- sep = "_"
- else:
- sep = ""
- return sep.join(map(str, version))
+def _version_nodot(version: PythonVersion) -> str:
+ return "".join(map(str, version))
-def sys_tags(**kwargs):
- # type: (bool) -> Iterator[Tag]
+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.
"""
- warn = _warn_keyword_parameter("sys_tags", kwargs)
interp_name = interpreter_name()
if interp_name == "cp":
- for tag in cpython_tags(warn=warn):
- yield tag
+ yield from cpython_tags(warn=warn)
else:
- for tag in generic_tags():
- yield tag
+ yield from generic_tags()
- for tag in compatible_tags():
- yield tag
+ yield from compatible_tags()
diff --git a/setuptools/_vendor/packaging/utils.py b/setuptools/_vendor/packaging/utils.py
index 19579c1a..bab11b80 100644
--- a/setuptools/_vendor/packaging/utils.py
+++ b/setuptools/_vendor/packaging/utils.py
@@ -1,65 +1,136 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import re
+from typing import FrozenSet, NewType, Tuple, Union, cast
-from ._typing import TYPE_CHECKING, cast
+from .tags import Tag, parse_tag
from .version import InvalidVersion, Version
-if TYPE_CHECKING: # pragma: no cover
- from typing import NewType, Union
+BuildTag = Union[Tuple[()], Tuple[int, str]]
+NormalizedName = NewType("NormalizedName", str)
+
+
+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.
+ """
- NormalizedName = NewType("NormalizedName", str)
_canonicalize_regex = re.compile(r"[-_.]+")
+# PEP 427: The build number must start with a digit.
+_build_tag_regex = re.compile(r"(\d+)(.*)")
-def canonicalize_name(name):
- # type: (str) -> NormalizedName
+def canonicalize_name(name: str) -> NormalizedName:
# This is taken from PEP 503.
value = _canonicalize_regex.sub("-", name).lower()
- return cast("NormalizedName", value)
+ return cast(NormalizedName, value)
-def canonicalize_version(_version):
- # type: (str) -> Union[Version, str]
+def canonicalize_version(version: Union[Version, str]) -> str:
"""
This is very similar to Version.__str__, but has one subtle difference
with the way it handles the release segment.
"""
-
- try:
- version = Version(_version)
- except InvalidVersion:
- # Legacy versions cannot be normalized
- return _version
+ if isinstance(version, str):
+ try:
+ parsed = Version(version)
+ except InvalidVersion:
+ # Legacy versions cannot be normalized
+ return version
+ else:
+ parsed = version
parts = []
# Epoch
- if version.epoch != 0:
- parts.append("{0}!".format(version.epoch))
+ if parsed.epoch != 0:
+ parts.append(f"{parsed.epoch}!")
# Release segment
# NB: This strips trailing '.0's to normalize
- parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release)))
+ parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release)))
# Pre-release
- if version.pre is not None:
- parts.append("".join(str(x) for x in version.pre))
+ if parsed.pre is not None:
+ parts.append("".join(str(x) for x in parsed.pre))
# Post-release
- if version.post is not None:
- parts.append(".post{0}".format(version.post))
+ if parsed.post is not None:
+ parts.append(f".post{parsed.post}")
# Development release
- if version.dev is not None:
- parts.append(".dev{0}".format(version.dev))
+ if parsed.dev is not None:
+ parts.append(f".dev{parsed.dev}")
# Local version segment
- if version.local is not None:
- parts.append("+{0}".format(version.local))
+ 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)
+ version = Version(parts[1])
+ 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)
+ version = Version(version_part)
+ return (name, version)
diff --git a/setuptools/_vendor/packaging/version.py b/setuptools/_vendor/packaging/version.py
index 00371e86..de9a09a4 100644
--- a/setuptools/_vendor/packaging/version.py
+++ b/setuptools/_vendor/packaging/version.py
@@ -1,52 +1,45 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import collections
import itertools
import re
+import warnings
+from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
-from ._structures import Infinity, NegativeInfinity
-from ._typing import TYPE_CHECKING
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
-
- from ._structures import InfinityType, NegativeInfinityType
-
- InfiniteTypes = Union[InfinityType, NegativeInfinityType]
- PrePostDevType = Union[InfiniteTypes, Tuple[str, int]]
- SubLocalType = Union[InfiniteTypes, int, str]
- LocalType = Union[
- NegativeInfinityType,
- Tuple[
- Union[
- SubLocalType,
- Tuple[SubLocalType, str],
- Tuple[NegativeInfinityType, SubLocalType],
- ],
- ...,
- ],
- ]
- CmpKey = Tuple[
- int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
- ]
- LegacyCmpKey = Tuple[int, Tuple[str, ...]]
- VersionComparisonMethod = Callable[
- [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
- ]
+from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
+InfiniteTypes = Union[InfinityType, NegativeInfinityType]
+PrePostDevType = Union[InfiniteTypes, Tuple[str, int]]
+SubLocalType = Union[InfiniteTypes, int, str]
+LocalType = Union[
+ NegativeInfinityType,
+ Tuple[
+ Union[
+ SubLocalType,
+ Tuple[SubLocalType, str],
+ Tuple[NegativeInfinityType, SubLocalType],
+ ],
+ ...,
+ ],
+]
+CmpKey = Tuple[
+ int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
+]
+LegacyCmpKey = Tuple[int, Tuple[str, ...]]
+VersionComparisonMethod = Callable[
+ [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
+]
_Version = collections.namedtuple(
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
)
-def parse(version):
- # type: (str) -> Union[LegacyVersion, Version]
+def parse(version: str) -> Union["LegacyVersion", "Version"]:
"""
Parse the given version string and return either a :class:`Version` object
or a :class:`LegacyVersion` object depending on if the given version is
@@ -64,112 +57,111 @@ class InvalidVersion(ValueError):
"""
-class _BaseVersion(object):
- _key = None # type: Union[CmpKey, LegacyCmpKey]
+class _BaseVersion:
+ _key: Union[CmpKey, LegacyCmpKey]
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._key)
- def __lt__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s < o)
+ # 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 __le__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s <= o)
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
- def __eq__(self, other):
- # type: (object) -> bool
- return self._compare(other, lambda s, o: s == o)
+ return self._key == other._key
- def __ge__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s >= o)
+ def __ge__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
- def __gt__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s > o)
+ return self._key >= other._key
- def __ne__(self, other):
- # type: (object) -> bool
- return self._compare(other, lambda s, o: s != o)
+ def __gt__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
+
+ return self._key > other._key
- def _compare(self, other, method):
- # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented]
+ def __ne__(self, other: object) -> bool:
if not isinstance(other, _BaseVersion):
return NotImplemented
- return method(self._key, other._key)
+ return self._key != other._key
class LegacyVersion(_BaseVersion):
- def __init__(self, version):
- # type: (str) -> None
+ def __init__(self, version: str) -> None:
self._version = str(version)
self._key = _legacy_cmpkey(self._version)
- def __str__(self):
- # type: () -> str
+ warnings.warn(
+ "Creating a LegacyVersion has been deprecated and will be "
+ "removed in the next major release",
+ DeprecationWarning,
+ )
+
+ def __str__(self) -> str:
return self._version
- def __repr__(self):
- # type: () -> str
- return "<LegacyVersion({0})>".format(repr(str(self)))
+ def __repr__(self) -> str:
+ return f"<LegacyVersion('{self}')>"
@property
- def public(self):
- # type: () -> str
+ def public(self) -> str:
return self._version
@property
- def base_version(self):
- # type: () -> str
+ def base_version(self) -> str:
return self._version
@property
- def epoch(self):
- # type: () -> int
+ def epoch(self) -> int:
return -1
@property
- def release(self):
- # type: () -> None
+ def release(self) -> None:
return None
@property
- def pre(self):
- # type: () -> None
+ def pre(self) -> None:
return None
@property
- def post(self):
- # type: () -> None
+ def post(self) -> None:
return None
@property
- def dev(self):
- # type: () -> None
+ def dev(self) -> None:
return None
@property
- def local(self):
- # type: () -> None
+ def local(self) -> None:
return None
@property
- def is_prerelease(self):
- # type: () -> bool
+ def is_prerelease(self) -> bool:
return False
@property
- def is_postrelease(self):
- # type: () -> bool
+ def is_postrelease(self) -> bool:
return False
@property
- def is_devrelease(self):
- # type: () -> bool
+ def is_devrelease(self) -> bool:
return False
@@ -184,8 +176,7 @@ _legacy_version_replacement_map = {
}
-def _parse_version_parts(s):
- # type: (str) -> Iterator[str]
+def _parse_version_parts(s: str) -> Iterator[str]:
for part in _legacy_version_component_re.split(s):
part = _legacy_version_replacement_map.get(part, part)
@@ -202,8 +193,7 @@ def _parse_version_parts(s):
yield "*final"
-def _legacy_cmpkey(version):
- # type: (str) -> LegacyCmpKey
+def _legacy_cmpkey(version: str) -> LegacyCmpKey:
# We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
# greater than or equal to 0. This will effectively put the LegacyVersion,
@@ -213,7 +203,7 @@ def _legacy_cmpkey(version):
# This scheme is taken from pkg_resources.parse_version setuptools prior to
# it's adoption of the packaging library.
- parts = [] # type: List[str]
+ parts: List[str] = []
for part in _parse_version_parts(version.lower()):
if part.startswith("*"):
# remove "-" before a prerelease tag
@@ -268,13 +258,12 @@ class Version(_BaseVersion):
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
- def __init__(self, version):
- # type: (str) -> None
+ def __init__(self, version: str) -> None:
# Validate the version and parse it into pieces
match = self._regex.search(version)
if not match:
- raise InvalidVersion("Invalid version: '{0}'".format(version))
+ raise InvalidVersion(f"Invalid version: '{version}'")
# Store the parsed out pieces of the version
self._version = _Version(
@@ -298,17 +287,15 @@ class Version(_BaseVersion):
self._version.local,
)
- def __repr__(self):
- # type: () -> str
- return "<Version({0})>".format(repr(str(self)))
+ def __repr__(self) -> str:
+ return f"<Version('{self}')>"
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
parts = []
# Epoch
if self.epoch != 0:
- parts.append("{0}!".format(self.epoch))
+ parts.append(f"{self.epoch}!")
# Release segment
parts.append(".".join(str(x) for x in self.release))
@@ -319,67 +306,59 @@ class Version(_BaseVersion):
# Post-release
if self.post is not None:
- parts.append(".post{0}".format(self.post))
+ parts.append(f".post{self.post}")
# Development release
if self.dev is not None:
- parts.append(".dev{0}".format(self.dev))
+ parts.append(f".dev{self.dev}")
# Local version segment
if self.local is not None:
- parts.append("+{0}".format(self.local))
+ parts.append(f"+{self.local}")
return "".join(parts)
@property
- def epoch(self):
- # type: () -> int
- _epoch = self._version.epoch # type: int
+ def epoch(self) -> int:
+ _epoch: int = self._version.epoch
return _epoch
@property
- def release(self):
- # type: () -> Tuple[int, ...]
- _release = self._version.release # type: Tuple[int, ...]
+ def release(self) -> Tuple[int, ...]:
+ _release: Tuple[int, ...] = self._version.release
return _release
@property
- def pre(self):
- # type: () -> Optional[Tuple[str, int]]
- _pre = self._version.pre # type: Optional[Tuple[str, int]]
+ def pre(self) -> Optional[Tuple[str, int]]:
+ _pre: Optional[Tuple[str, int]] = self._version.pre
return _pre
@property
- def post(self):
- # type: () -> Optional[Tuple[str, int]]
+ def post(self) -> Optional[int]:
return self._version.post[1] if self._version.post else None
@property
- def dev(self):
- # type: () -> Optional[Tuple[str, int]]
+ def dev(self) -> Optional[int]:
return self._version.dev[1] if self._version.dev else None
@property
- def local(self):
- # type: () -> Optional[str]
+ def local(self) -> Optional[str]:
if self._version.local:
return ".".join(str(x) for x in self._version.local)
else:
return None
@property
- def public(self):
- # type: () -> str
+ def public(self) -> str:
return str(self).split("+", 1)[0]
@property
- def base_version(self):
- # type: () -> str
+ def base_version(self) -> str:
parts = []
# Epoch
if self.epoch != 0:
- parts.append("{0}!".format(self.epoch))
+ parts.append(f"{self.epoch}!")
# Release segment
parts.append(".".join(str(x) for x in self.release))
@@ -387,41 +366,33 @@ class Version(_BaseVersion):
return "".join(parts)
@property
- def is_prerelease(self):
- # type: () -> bool
+ def is_prerelease(self) -> bool:
return self.dev is not None or self.pre is not None
@property
- def is_postrelease(self):
- # type: () -> bool
+ def is_postrelease(self) -> bool:
return self.post is not None
@property
- def is_devrelease(self):
- # type: () -> bool
+ def is_devrelease(self) -> bool:
return self.dev is not None
@property
- def major(self):
- # type: () -> int
+ def major(self) -> int:
return self.release[0] if len(self.release) >= 1 else 0
@property
- def minor(self):
- # type: () -> int
+ def minor(self) -> int:
return self.release[1] if len(self.release) >= 2 else 0
@property
- def micro(self):
- # type: () -> int
+ def micro(self) -> int:
return self.release[2] if len(self.release) >= 3 else 0
def _parse_letter_version(
- letter, # type: str
- number, # type: Union[str, bytes, SupportsInt]
-):
- # type: (...) -> Optional[Tuple[str, int]]
+ letter: str, number: Union[str, bytes, SupportsInt]
+) -> Optional[Tuple[str, int]]:
if letter:
# We consider there to be an implicit 0 in a pre-release if there is
@@ -458,8 +429,7 @@ def _parse_letter_version(
_local_version_separators = re.compile(r"[\._-]")
-def _parse_local_version(local):
- # type: (str) -> Optional[LocalType]
+def _parse_local_version(local: str) -> Optional[LocalType]:
"""
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
"""
@@ -472,14 +442,13 @@ def _parse_local_version(local):
def _cmpkey(
- epoch, # type: int
- release, # type: Tuple[int, ...]
- pre, # type: Optional[Tuple[str, int]]
- post, # type: Optional[Tuple[str, int]]
- dev, # type: Optional[Tuple[str, int]]
- local, # type: Optional[Tuple[SubLocalType]]
-):
- # type: (...) -> CmpKey
+ epoch: int,
+ release: Tuple[int, ...],
+ pre: Optional[Tuple[str, int]],
+ post: Optional[Tuple[str, int]],
+ dev: Optional[Tuple[str, int]],
+ local: Optional[Tuple[SubLocalType]],
+) -> 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
@@ -495,7 +464,7 @@ def _cmpkey(
# if there is not a pre or a post segment. If we have one of those then
# the normal sorting rules will handle this case correctly.
if pre is None and post is None and dev is not None:
- _pre = NegativeInfinity # type: PrePostDevType
+ _pre: PrePostDevType = NegativeInfinity
# Versions without a pre-release (except as noted above) should sort after
# those with one.
elif pre is None:
@@ -505,21 +474,21 @@ def _cmpkey(
# Versions without a post segment should sort before those with one.
if post is None:
- _post = NegativeInfinity # type: PrePostDevType
+ _post: PrePostDevType = NegativeInfinity
else:
_post = post
# Versions without a development segment should sort after those with one.
if dev is None:
- _dev = Infinity # type: PrePostDevType
+ _dev: PrePostDevType = Infinity
else:
_dev = dev
if local is None:
# Versions without a local segment should sort before those with one.
- _local = NegativeInfinity # type: LocalType
+ _local: LocalType = NegativeInfinity
else:
# Versions with a local segment need that segment parsed to implement
# the sorting rules in PEP440.
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst b/setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
new file mode 100644
index 00000000..e1187231
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
@@ -0,0 +1,3 @@
+UNKNOWN
+
+
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER b/setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt b/setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt
new file mode 100644
index 00000000..bbc959e0
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt
@@ -0,0 +1,18 @@
+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/setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA b/setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA
new file mode 100644
index 00000000..a15c350e
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA
@@ -0,0 +1,30 @@
+Metadata-Version: 2.0
+Name: pyparsing
+Version: 2.2.1
+Summary: Python parsing module
+Home-page: https://github.com/pyparsing/pyparsing/
+Author: Paul McGuire
+Author-email: ptmcg@users.sourceforge.net
+License: MIT License
+Download-URL: https://pypi.org/project/pyparsing/
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
+
+UNKNOWN
+
+
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD b/setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD
new file mode 100644
index 00000000..09cc30e3
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD
@@ -0,0 +1,11 @@
+__pycache__/pyparsing.cpython-310.pyc,,
+pyparsing-2.2.1.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10
+pyparsing-2.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pyparsing-2.2.1.dist-info/LICENSE.txt,sha256=081Pq74Spe1XdwrGkewNKSqa078kLIh7UWI-wVjdj8I,1041
+pyparsing-2.2.1.dist-info/METADATA,sha256=I0jhx9vpUYlQXjn4gVDnFFoAt3nNrxwR4iuqA_pknYs,1091
+pyparsing-2.2.1.dist-info/RECORD,,
+pyparsing-2.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pyparsing-2.2.1.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
+pyparsing-2.2.1.dist-info/metadata.json,sha256=v1_77-dSdajUZSItSJg8Ov9M713STY3PzhyrRvs1ax4,1185
+pyparsing-2.2.1.dist-info/top_level.txt,sha256=eUOjGzJVhlQ3WS2rFAy2mN3LX_7FKTM5GSJ04jfnLmU,10
+pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED b/setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL b/setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL
new file mode 100644
index 00000000..7332a419
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json b/setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json
new file mode 100644
index 00000000..b760b766
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7"], "download_url": "https://pypi.org/project/pyparsing/", "extensions": {"python.details": {"contacts": [{"email": "ptmcg@users.sourceforge.net", "name": "Paul McGuire", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://github.com/pyparsing/pyparsing/"}}}, "generator": "bdist_wheel (0.30.0)", "license": "MIT License", "metadata_version": "2.0", "name": "pyparsing", "requires_python": ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", "summary": "Python parsing module", "version": "2.2.1"} \ No newline at end of file
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt b/setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt
new file mode 100644
index 00000000..210dfec5
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+pyparsing
diff --git a/setuptools/_vendor/pyparsing.py b/setuptools/_vendor/pyparsing.py
index 4cae7883..cf75e1e5 100644
--- a/setuptools/_vendor/pyparsing.py
+++ b/setuptools/_vendor/pyparsing.py
@@ -1625,7 +1625,7 @@ class ParserElement(object):
(see L{I{parseWithTabs}<parseWithTabs>})
- define your parse action using the full C{(s,loc,toks)} signature, and
reference the input string using the parse action's C{s} argument
- - explicitly expand the tabs in your input string before calling
+ - explictly expand the tabs in your input string before calling
C{parseString}
Example::
diff --git a/setuptools/_vendor/vendored.txt b/setuptools/_vendor/vendored.txt
index d9804741..0639990b 100644
--- a/setuptools/_vendor/vendored.txt
+++ b/setuptools/_vendor/vendored.txt
@@ -1,4 +1,9 @@
-packaging==20.4
+packaging==21.2
pyparsing==2.2.1
ordered-set==3.1.1
more_itertools==8.8.0
+importlib_resources==5.4.0
+importlib_metadata==4.10.1
+jaraco.text==3.7.0
+# required for importlib_resources on older Pythons
+zipp==3.7.0
diff --git a/setuptools/_vendor/zipp-3.7.0.dist-info/INSTALLER b/setuptools/_vendor/zipp-3.7.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/zipp-3.7.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/zipp-3.7.0.dist-info/LICENSE b/setuptools/_vendor/zipp-3.7.0.dist-info/LICENSE
new file mode 100644
index 00000000..353924be
--- /dev/null
+++ b/setuptools/_vendor/zipp-3.7.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright Jason R. Coombs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/setuptools/_vendor/zipp-3.7.0.dist-info/METADATA b/setuptools/_vendor/zipp-3.7.0.dist-info/METADATA
new file mode 100644
index 00000000..b1308b5f
--- /dev/null
+++ b/setuptools/_vendor/zipp-3.7.0.dist-info/METADATA
@@ -0,0 +1,58 @@
+Metadata-Version: 2.1
+Name: zipp
+Version: 3.7.0
+Summary: Backport of pathlib-compatible object wrapper for zip files
+Home-page: https://github.com/jaraco/zipp
+Author: Jason R. Coombs
+Author-email: jaraco@jaraco.com
+License: UNKNOWN
+Platform: UNKNOWN
+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
+Requires-Python: >=3.7
+License-File: LICENSE
+Provides-Extra: docs
+Requires-Dist: sphinx ; extra == 'docs'
+Requires-Dist: jaraco.packaging (>=8.2) ; extra == 'docs'
+Requires-Dist: rst.linker (>=1.9) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: pytest (>=6) ; extra == 'testing'
+Requires-Dist: pytest-checkdocs (>=2.4) ; extra == 'testing'
+Requires-Dist: pytest-flake8 ; extra == 'testing'
+Requires-Dist: pytest-cov ; extra == 'testing'
+Requires-Dist: pytest-enabler (>=1.0.1) ; extra == 'testing'
+Requires-Dist: jaraco.itertools ; extra == 'testing'
+Requires-Dist: func-timeout ; extra == 'testing'
+Requires-Dist: pytest-black (>=0.3.7) ; (platform_python_implementation != "PyPy") and extra == 'testing'
+Requires-Dist: pytest-mypy ; (platform_python_implementation != "PyPy") and extra == 'testing'
+
+.. image:: https://img.shields.io/pypi/v/zipp.svg
+ :target: `PyPI link`_
+
+.. image:: https://img.shields.io/pypi/pyversions/zipp.svg
+ :target: `PyPI link`_
+
+.. _PyPI link: https://pypi.org/project/zipp
+
+.. image:: https://github.com/jaraco/zipp/workflows/tests/badge.svg
+ :target: https://github.com/jaraco/zipp/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+ :alt: Code style: Black
+
+.. .. image:: https://readthedocs.org/projects/skeleton/badge/?version=latest
+.. :target: https://skeleton.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2021-informational
+ :target: https://blog.jaraco.com/skeleton
+
+
+A pathlib-compatible Zipfile object wrapper. Official backport of the standard library
+`Path object <https://docs.python.org/3.8/library/zipfile.html#path-objects>`_.
+
+
diff --git a/setuptools/_vendor/zipp-3.7.0.dist-info/RECORD b/setuptools/_vendor/zipp-3.7.0.dist-info/RECORD
new file mode 100644
index 00000000..38d0b21a
--- /dev/null
+++ b/setuptools/_vendor/zipp-3.7.0.dist-info/RECORD
@@ -0,0 +1,9 @@
+__pycache__/zipp.cpython-310.pyc,,
+zipp-3.7.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+zipp-3.7.0.dist-info/LICENSE,sha256=2z8CRrH5J48VhFuZ_sR4uLUG63ZIeZNyL4xuJUKF-vg,1050
+zipp-3.7.0.dist-info/METADATA,sha256=ZLzgaXTyZX_MxTU0lcGfhdPY4CjFrT_3vyQ2Fo49pl8,2261
+zipp-3.7.0.dist-info/RECORD,,
+zipp-3.7.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+zipp-3.7.0.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
+zipp-3.7.0.dist-info/top_level.txt,sha256=iAbdoSHfaGqBfVb2XuR9JqSQHCoOsOtG6y9C_LSpqFw,5
+zipp.py,sha256=ajztOH-9I7KA_4wqDYygtHa6xUBVZgFpmZ8FE74HHHI,8425
diff --git a/setuptools/_vendor/zipp-3.7.0.dist-info/REQUESTED b/setuptools/_vendor/zipp-3.7.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/zipp-3.7.0.dist-info/REQUESTED
diff --git a/setuptools/_vendor/zipp-3.7.0.dist-info/WHEEL b/setuptools/_vendor/zipp-3.7.0.dist-info/WHEEL
new file mode 100644
index 00000000..becc9a66
--- /dev/null
+++ b/setuptools/_vendor/zipp-3.7.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/zipp-3.7.0.dist-info/top_level.txt b/setuptools/_vendor/zipp-3.7.0.dist-info/top_level.txt
new file mode 100644
index 00000000..e82f676f
--- /dev/null
+++ b/setuptools/_vendor/zipp-3.7.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+zipp
diff --git a/setuptools/_vendor/zipp.py b/setuptools/_vendor/zipp.py
new file mode 100644
index 00000000..26b723c1
--- /dev/null
+++ b/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/setuptools/archive_util.py b/setuptools/archive_util.py
index 0f702848..73b2db75 100644
--- a/setuptools/archive_util.py
+++ b/setuptools/archive_util.py
@@ -8,7 +8,7 @@ import posixpath
import contextlib
from distutils.errors import DistutilsError
-from pkg_resources import ensure_directory
+from ._path import ensure_directory
__all__ = [
"unpack_archive", "unpack_zipfile", "unpack_tarfile", "default_filter",
diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py
index d0ac613b..1daa77c9 100644
--- a/setuptools/build_meta.py
+++ b/setuptools/build_meta.py
@@ -37,8 +37,7 @@ import warnings
import setuptools
import distutils
-
-from pkg_resources import parse_requirements
+from ._reqs import parse_strings
__all__ = ['get_requires_for_build_sdist',
'get_requires_for_build_wheel',
@@ -56,7 +55,7 @@ class SetupRequirementsError(BaseException):
class Distribution(setuptools.dist.Distribution):
def fetch_build_eggs(self, specifiers):
- specifier_list = list(map(str, parse_requirements(specifiers)))
+ specifier_list = list(parse_strings(specifiers))
raise SetupRequirementsError(specifier_list)
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index e6b1609f..11a1c6be 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -11,9 +11,10 @@ import re
import textwrap
import marshal
-from pkg_resources import get_build_platform, Distribution, ensure_directory
+from pkg_resources import get_build_platform, Distribution
from setuptools.extension import Library
from setuptools import Command
+from .._path import ensure_directory
from sysconfig import get_path, get_python_version
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 1aed0e87..b1260dcd 100644
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -6,7 +6,7 @@ A tool for doing automatic download/extract/build of distutils-based Python
packages. For detailed documentation, see the accompanying EasyInstall.txt
file, or visit the `EasyInstall home page`__.
-__ https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html
+__ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
"""
@@ -17,10 +17,10 @@ from distutils.errors import (
DistutilsArgError, DistutilsOptionError,
DistutilsError, DistutilsPlatformError,
)
-from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS
from distutils import log, dir_util
from distutils.command.build_scripts import first_line_re
from distutils.spawn import find_executable
+from distutils.command import install
import sys
import os
import zipimport
@@ -39,9 +39,10 @@ import subprocess
import shlex
import io
import configparser
+import sysconfig
-from sysconfig import get_config_vars, get_path
+from sysconfig import get_path
from setuptools import SetuptoolsDeprecationWarning
@@ -55,12 +56,13 @@ from setuptools.package_index import (
from setuptools.command import bdist_egg, egg_info
from setuptools.wheel import Wheel
from pkg_resources import (
- yield_lines, normalize_path, resource_string, ensure_directory,
+ yield_lines, normalize_path, resource_string,
get_distribution, find_distributions, Environment, Requirement,
Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound,
VersionConflict, DEVELOP_DIST,
)
import pkg_resources
+from .._path import ensure_directory
# Turn on PEP440Warnings
warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)
@@ -236,22 +238,34 @@ class easy_install(Command):
self.version and self._render_version()
py_version = sys.version.split()[0]
- prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')
- self.config_vars = {
+ 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': py_version[0:3],
- 'py_version_nodot': py_version[0] + py_version[2],
- 'sys_prefix': prefix,
- 'prefix': prefix,
- 'sys_exec_prefix': exec_prefix,
- 'exec_prefix': exec_prefix,
+ '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 python 3.2+ has abiflags
'abiflags': getattr(sys, 'abiflags', ''),
- }
+ '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('.', ''),
+ )
if site.ENABLE_USER_SITE:
self.config_vars['userbase'] = self.install_userbase
@@ -371,7 +385,7 @@ class easy_install(Command):
msg = "User base directory is not specified"
raise DistutilsPlatformError(msg)
self.install_base = self.install_platbase = self.install_userbase
- scheme_name = os.name.replace('posix', 'unix') + '_user'
+ scheme_name = f'{os.name}_user'
self.select_scheme(scheme_name)
def _expand_attrs(self, attrs):
@@ -519,7 +533,7 @@ class easy_install(Command):
For information on other options, you may wish to consult the
documentation at:
- https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html
+ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
Please make the appropriate changes for your system and try again.
""").lstrip() # noqa
@@ -711,13 +725,11 @@ class easy_install(Command):
return dist
def select_scheme(self, name):
- """Sets the install directories by applying the install schemes."""
- # it's the caller's problem if they supply a bad name!
- scheme = INSTALL_SCHEMES[name]
- for key in SCHEME_KEYS:
- attrname = 'install_' + key
- if getattr(self, attrname) is None:
- setattr(self, attrname, scheme[key])
+ 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
@@ -1312,7 +1324,7 @@ class easy_install(Command):
* You can set up the installation directory to support ".pth" files by
using one of the approaches described here:
- https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html#custom-installation-locations
+ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html#custom-installation-locations
Please make the appropriate changes for your system and try again.
@@ -1323,7 +1335,7 @@ class easy_install(Command):
if not self.user:
return
home = convert_path(os.path.expanduser("~"))
- for name, path in self.config_vars.items():
+ 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)
@@ -1345,7 +1357,7 @@ class easy_install(Command):
if self.prefix:
# Set default install_dir/scripts from --prefix
- config_vars = config_vars.copy()
+ 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():
@@ -1572,7 +1584,7 @@ class PthDistributions(Environment):
self.sitedirs = list(map(normalize_path, sitedirs))
self.basedir = normalize_path(os.path.dirname(self.filename))
self._load()
- Environment.__init__(self, [], None, None)
+ super().__init__([], None, None)
for path in yield_lines(self.paths):
list(map(self.add, find_distributions(path, True)))
@@ -2293,6 +2305,13 @@ def current_umask():
return tmp
+def only_strs(values):
+ """
+ Exclude non-str values. Ref #3063.
+ """
+ return filter(lambda val: isinstance(val, str), values)
+
+
class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning):
"""
Warning for EasyInstall deprecations, bypassing suppression.
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index d7abfe38..d55797bc 100644
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -23,7 +23,7 @@ from setuptools.command.sdist import walk_revctrl
from setuptools.command.setopt import edit_config
from setuptools.command import bdist_egg
from pkg_resources import (
- parse_requirements, safe_name, parse_version,
+ Requirement, safe_name, parse_version,
safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename)
import setuptools.unicode_utils as unicode_utils
from setuptools.glob import glob
@@ -205,12 +205,8 @@ class egg_info(InfoCommon, Command):
try:
is_version = isinstance(parsed_version, packaging.version.Version)
- spec = (
- "%s==%s" if is_version else "%s===%s"
- )
- list(
- parse_requirements(spec % (self.egg_name, self.egg_version))
- )
+ spec = "%s==%s" if is_version else "%s===%s"
+ Requirement(spec % (self.egg_name, self.egg_version))
except ValueError as e:
raise distutils.errors.DistutilsOptionError(
"Invalid distribution name or version syntax: %s-%s" %
diff --git a/setuptools/command/install_egg_info.py b/setuptools/command/install_egg_info.py
index edc4718b..65ede406 100644
--- a/setuptools/command/install_egg_info.py
+++ b/setuptools/command/install_egg_info.py
@@ -4,6 +4,7 @@ import os
from setuptools import Command
from setuptools import namespaces
from setuptools.archive_util import unpack_archive
+from .._path import ensure_directory
import pkg_resources
@@ -37,7 +38,7 @@ class install_egg_info(namespaces.Installer, Command):
elif os.path.exists(self.target):
self.execute(os.unlink, (self.target,), "Removing " + self.target)
if not self.dry_run:
- pkg_resources.ensure_directory(self.target)
+ ensure_directory(self.target)
self.execute(
self.copytree, (), "Copying %s to %s" % (self.source, self.target)
)
diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py
index 9cd8eb06..aeb0e424 100644
--- a/setuptools/command/install_scripts.py
+++ b/setuptools/command/install_scripts.py
@@ -4,7 +4,8 @@ from distutils.errors import DistutilsModuleError
import os
import sys
-from pkg_resources import Distribution, PathMetadata, ensure_directory
+from pkg_resources import Distribution, PathMetadata
+from .._path import ensure_directory
class install_scripts(orig.install_scripts):
diff --git a/setuptools/config.py b/setuptools/config.py
index e3e44c25..b4e968e5 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -13,7 +13,7 @@ from glob import iglob
import contextlib
from distutils.errors import DistutilsOptionError, DistutilsFileError
-from setuptools.extern.packaging.version import LegacyVersion, parse
+from setuptools.extern.packaging.version import Version, InvalidVersion
from setuptools.extern.packaging.specifiers import SpecifierSet
@@ -585,7 +585,9 @@ class ConfigMetadataHandler(ConfigHandler):
version = version.strip()
# Be strict about versions loaded from file because it's easy to
# accidentally include newlines and other unintended content
- if isinstance(parse(version), LegacyVersion):
+ try:
+ Version(version)
+ except InvalidVersion:
tmpl = (
'Version loaded from {value} does not '
'comply with PEP 440: {version}'
diff --git a/setuptools/depends.py b/setuptools/depends.py
index 8be6928a..adffd12d 100644
--- a/setuptools/depends.py
+++ b/setuptools/depends.py
@@ -2,7 +2,8 @@ import sys
import marshal
import contextlib
import dis
-from distutils.version import StrictVersion
+
+from setuptools.extern.packaging import version
from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
from . import _imp
@@ -21,7 +22,7 @@ class Require:
attribute=None, format=None):
if format is None and requested_version is not None:
- format = StrictVersion
+ format = version.Version
if format is not None:
requested_version = format(requested_version)
@@ -40,7 +41,7 @@ class Require:
def version_ok(self, version):
"""Is 'version' sufficiently up-to-date?"""
return self.attribute is None or self.format is None or \
- str(version) != "unknown" and version >= self.requested_version
+ 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'
@@ -78,7 +79,7 @@ class Require:
version = self.get_version(paths)
if version is None:
return False
- return self.version_ok(version)
+ return self.version_ok(str(version))
def maybe_close(f):
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 848d6b0f..733ae14f 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -25,7 +25,6 @@ from email import message_from_file
from distutils.errors import DistutilsOptionError, DistutilsSetupError
from distutils.util import rfc822_escape
-from distutils.version import StrictVersion
from setuptools.extern import packaging
from setuptools.extern import ordered_set
@@ -39,6 +38,8 @@ from setuptools import windows_support
from setuptools.monkey import get_unpatched
from setuptools.config import parse_configuration
import pkg_resources
+from setuptools.extern.packaging import version
+from . import _reqs
if TYPE_CHECKING:
from email.message import Message
@@ -55,7 +56,7 @@ def _get_unpatched(cls):
def get_metadata_version(self):
mv = getattr(self, 'metadata_version', None)
if mv is None:
- mv = StrictVersion('2.1')
+ mv = version.Version('2.1')
self.metadata_version = mv
return mv
@@ -103,7 +104,7 @@ def read_pkg_file(self, file):
"""Reads the metadata values from a file object."""
msg = message_from_file(file)
- self.metadata_version = StrictVersion(msg['metadata-version'])
+ self.metadata_version = 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')
@@ -113,15 +114,14 @@ def read_pkg_file(self, file):
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')
- if 'download-url' in msg:
- self.download_url = _read_field_from_msg(msg, 'download-url')
- else:
- self.download_url = None
-
self.long_description = _read_field_unescaped_from_msg(msg, 'description')
- if self.long_description is None and self.metadata_version >= StrictVersion('2.1'):
+ if (
+ self.long_description is None and
+ self.metadata_version >= version.Version('2.1')
+ ):
self.long_description = _read_payload_from_msg(msg)
self.description = _read_field_from_msg(msg, 'summary')
@@ -132,7 +132,7 @@ def read_pkg_file(self, file):
self.classifiers = _read_list_from_msg(msg, 'classifier')
# PEP 314 - these fields only exist in 1.1
- if self.metadata_version == StrictVersion('1.1'):
+ if self.metadata_version == 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')
@@ -145,11 +145,14 @@ def read_pkg_file(self, file):
def single_line(val):
- """Validate that the value does not have line breaks."""
- # Ref: https://github.com/pypa/setuptools/issues/1390
+ """
+ Quick and dirty validation for Summary pypa/setuptools#1390.
+ """
if '\n' in val:
- raise ValueError('Newlines are not allowed')
-
+ # TODO: Replace with `raise ValueError("newlines not allowed")`
+ # after reviewing #2893.
+ warnings.warn("newlines not allowed and will break in the future")
+ val = val.strip().split('\n')[0]
return val
@@ -165,9 +168,10 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
write_field('Name', self.get_name())
write_field('Version', self.get_version())
write_field('Summary', single_line(self.get_description()))
- write_field('Home-page', self.get_url())
optional_fields = (
+ ('Home-page', 'url'),
+ ('Download-URL', 'download_url'),
('Author', 'author'),
('Author-email', 'author_email'),
('Maintainer', 'maintainer'),
@@ -181,8 +185,6 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
license = rfc822_escape(self.get_license())
write_field('License', license)
- if self.download_url:
- write_field('Download-URL', self.download_url)
for project_url in self.project_urls.items():
write_field('Project-URL', '%s, %s' % project_url)
@@ -279,7 +281,7 @@ def _check_extra(extra, reqs):
name, sep, marker = extra.partition(':')
if marker and pkg_resources.invalid_marker(marker):
raise DistutilsSetupError("Invalid environment marker: " + marker)
- list(pkg_resources.parse_requirements(reqs))
+ list(_reqs.parse(reqs))
def assert_bool(dist, attr, value):
@@ -299,7 +301,7 @@ def invalid_unless_false(dist, attr, value):
def check_requirements(dist, attr, value):
"""Verify that install_requires is a valid requirements list"""
try:
- list(pkg_resources.parse_requirements(value))
+ list(_reqs.parse(value))
if isinstance(value, (dict, set)):
raise TypeError("Unordered types are not allowed")
except (TypeError, ValueError) as error:
@@ -466,6 +468,19 @@ class Distribution(_Distribution):
)
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.
@@ -538,7 +553,7 @@ class Distribution(_Distribution):
for section, v in spec_ext_reqs.items():
# Do not strip empty sections.
self._tmp_extras_require[section]
- for r in pkg_resources.parse_requirements(v):
+ for r in _reqs.parse(v):
suffix = self._suffix_for(r)
self._tmp_extras_require[section + suffix].append(r)
@@ -564,7 +579,7 @@ class Distribution(_Distribution):
return not req.marker
spec_inst_reqs = getattr(self, 'install_requires', None) or ()
- inst_reqs = list(pkg_resources.parse_requirements(spec_inst_reqs))
+ inst_reqs = list(_reqs.parse(spec_inst_reqs))
simple_reqs = filter(is_simple_req, inst_reqs)
complex_reqs = itertools.filterfalse(is_simple_req, inst_reqs)
self.install_requires = list(map(str, simple_reqs))
@@ -804,7 +819,7 @@ class Distribution(_Distribution):
def fetch_build_eggs(self, requires):
"""Resolve pre-setup requirements"""
resolved_dists = pkg_resources.working_set.resolve(
- pkg_resources.parse_requirements(requires),
+ _reqs.parse(requires),
installer=self.fetch_build_egg,
replace_conflicting=True,
)
diff --git a/setuptools/extension.py b/setuptools/extension.py
index 1820722a..f696c9c1 100644
--- a/setuptools/extension.py
+++ b/setuptools/extension.py
@@ -34,7 +34,7 @@ class Extension(_Extension):
# The *args is needed for compatibility as calls may use positional
# arguments. py_limited_api may be set only via keyword.
self.py_limited_api = kw.pop("py_limited_api", False)
- _Extension.__init__(self, name, sources, *args, **kw)
+ super().__init__(name, sources, *args, **kw)
def _convert_pyx_sources_to_lang(self):
"""
diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py
index baca1afa..3570a3b4 100644
--- a/setuptools/extern/__init__.py
+++ b/setuptools/extern/__init__.py
@@ -69,5 +69,8 @@ class VendorImporter:
sys.meta_path.append(self)
-names = 'packaging', 'pyparsing', 'ordered_set', 'more_itertools',
+names = (
+ 'packaging', 'pyparsing', 'ordered_set', 'more_itertools', 'importlib_metadata',
+ 'zipp', 'importlib_resources', 'jaraco',
+)
VendorImporter(__name__, names, 'setuptools._vendor').install()
diff --git a/setuptools/installer.py b/setuptools/installer.py
index 57e2b587..b7096df1 100644
--- a/setuptools/installer.py
+++ b/setuptools/installer.py
@@ -3,11 +3,13 @@ import os
import subprocess
import sys
import tempfile
+import warnings
from distutils import log
from distutils.errors import DistutilsError
import pkg_resources
from setuptools.wheel import Wheel
+from ._deprecation_warning import SetuptoolsDeprecationWarning
def _fixup_find_links(find_links):
@@ -22,6 +24,11 @@ def fetch_build_egg(dist, req): # noqa: C901 # is too complex (16) # FIXME
"""Fetch an egg needed for building.
Use pip/wheel to fetch/build a wheel."""
+ warnings.warn(
+ "setuptools.installer is deprecated. Requirements should "
+ "be satisfied by a PEP 517 installer.",
+ SetuptoolsDeprecationWarning,
+ )
# Warn if wheel is not available
try:
pkg_resources.get_distribution('wheel')
diff --git a/setuptools/logging.py b/setuptools/logging.py
new file mode 100644
index 00000000..15b57613
--- /dev/null
+++ b/setuptools/logging.py
@@ -0,0 +1,36 @@
+import sys
+import logging
+import distutils.log
+from . import monkey
+
+
+def _not_warning(record):
+ return record.levelno < logging.WARNING
+
+
+def configure():
+ """
+ Configure logging to emit warning and above to stderr
+ and everything else to stdout. This behavior is provided
+ for compatibilty with distutils.log but may change in
+ the future.
+ """
+ err_handler = logging.StreamHandler()
+ err_handler.setLevel(logging.WARNING)
+ out_handler = logging.StreamHandler(sys.stdout)
+ out_handler.addFilter(_not_warning)
+ handlers = err_handler, out_handler
+ logging.basicConfig(
+ format="{message}", style='{', handlers=handlers, level=logging.DEBUG)
+ monkey.patch_func(set_threshold, distutils.log, 'set_threshold')
+
+ # For some reason `distutils.log` module is getting cached in `distutils.dist`
+ # and then loaded again when patched,
+ # implying: id(distutils.log) != id(distutils.dist.log).
+ # Make sure the same module object is used everywhere:
+ distutils.dist.log = distutils.log
+
+
+def set_threshold(level):
+ logging.root.setLevel(level*10)
+ return set_threshold.unpatched(level)
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index bef2ef81..24a7f306 100644
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -21,7 +21,7 @@ import setuptools
from pkg_resources import (
CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
Environment, find_distributions, safe_name, safe_version,
- to_filename, Requirement, DEVELOP_DIST, EGG_DIST,
+ to_filename, Requirement, DEVELOP_DIST, EGG_DIST, parse_version,
)
from distutils import log
from distutils.errors import DistutilsError
@@ -281,7 +281,7 @@ class PackageIndex(Environment):
self, index_url="https://pypi.org/simple/", hosts=('*',),
ca_bundle=None, verify_ssl=True, *args, **kw
):
- Environment.__init__(self, *args, **kw)
+ super().__init__(*args, **kw)
self.index_url = index_url + "/" [:not index_url.endswith('/')]
self.scanned_urls = {}
self.fetched_urls = {}
@@ -291,8 +291,10 @@ class PackageIndex(Environment):
self.opener = urllib.request.urlopen
def add(self, dist):
- # ignore invalid pbr version
- if dist.version == '0.5.2.5.g5b3e942':
+ # ignore invalid versions
+ try:
+ parse_version(dist.version)
+ except Exception:
return
return super().add(dist)
@@ -996,7 +998,7 @@ class PyPIConfig(configparser.RawConfigParser):
Load from ~/.pypirc
"""
defaults = dict.fromkeys(['username', 'password', 'repository'], '')
- configparser.RawConfigParser.__init__(self, defaults)
+ super().__init__(defaults)
rc = os.path.join(os.path.expanduser('~'), '.pypirc')
if os.path.exists(rc):
diff --git a/setuptools/tests/contexts.py b/setuptools/tests/contexts.py
index 51ce8984..58948824 100644
--- a/setuptools/tests/contexts.py
+++ b/setuptools/tests/contexts.py
@@ -7,6 +7,7 @@ import site
import io
import pkg_resources
+from filelock import FileLock
@contextlib.contextmanager
@@ -96,3 +97,29 @@ def suppress_exceptions(*excs):
yield
except excs:
pass
+
+
+def multiproc(request):
+ """
+ Return True if running under xdist and multiple
+ workers are used.
+ """
+ try:
+ worker_id = request.getfixturevalue('worker_id')
+ except Exception:
+ return False
+ return worker_id != 'master'
+
+
+@contextlib.contextmanager
+def session_locked_tmp_dir(request, tmp_path_factory, name):
+ """Uses a file lock to guarantee only one worker can access a temp dir"""
+ # get the temp directory shared by all workers
+ base = tmp_path_factory.getbasetemp()
+ shared_dir = base.parent if multiproc(request) else base
+
+ locked_dir = shared_dir / name
+ with FileLock(locked_dir.with_suffix(".lock")):
+ # ^-- prevent multiple workers to access the directory at once
+ locked_dir.mkdir(exist_ok=True, parents=True)
+ yield locked_dir
diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py
index c0274c33..a0c0ec6e 100644
--- a/setuptools/tests/environment.py
+++ b/setuptools/tests/environment.py
@@ -1,9 +1,25 @@
import os
import sys
+import subprocess
import unicodedata
-
from subprocess import Popen as _Popen, PIPE as _PIPE
+import jaraco.envs
+
+
+class VirtualEnv(jaraco.envs.VirtualEnv):
+ name = '.env'
+ # Some version of PyPy will import distutils on startup, implicitly
+ # importing setuptools, and thus leading to BackendInvalid errors
+ # when upgrading Setuptools. Bypass this behavior by avoiding the
+ # early availability and need to upgrade.
+ create_opts = ['--no-setuptools']
+
+ def run(self, cmd, *args, **kwargs):
+ cmd = [self.exe(cmd[0])] + cmd[1:]
+ kwargs = {"cwd": self.root, **kwargs} # Allow overriding
+ return subprocess.check_output(cmd, *args, **kwargs)
+
def _which_dirs(cmd):
result = set()
diff --git a/setuptools/tests/fixtures.py b/setuptools/tests/fixtures.py
index a5a172e0..7599e655 100644
--- a/setuptools/tests/fixtures.py
+++ b/setuptools/tests/fixtures.py
@@ -1,11 +1,11 @@
import contextlib
import sys
-import shutil
import subprocess
import pytest
+import path
-from . import contexts
+from . import contexts, environment
@pytest.fixture
@@ -28,22 +28,6 @@ def tmpdir_cwd(tmpdir):
yield orig
-@pytest.fixture
-def tmp_src(request, tmp_path):
- """Make a copy of the source dir under `$tmp/src`.
-
- This fixture is useful whenever it's necessary to run `setup.py`
- or `pip install` against the source directory when there's no
- control over the number of simultaneous invocations. Such
- concurrent runs create and delete directories with the same names
- under the target directory and so they influence each other's runs
- when they are not being executed sequentially.
- """
- tmp_src_path = tmp_path / 'src'
- shutil.copytree(request.config.rootdir, tmp_src_path)
- return tmp_src_path
-
-
@pytest.fixture(autouse=True, scope="session")
def workaround_xdist_376(request):
"""
@@ -72,3 +56,66 @@ def sample_project(tmp_path):
except Exception:
pytest.skip("Unable to clone sampleproject")
return tmp_path / 'sampleproject'
+
+
+# sdist and wheel artifacts should be stable across a round of tests
+# so we can build them once per session and use the files as "readonly"
+
+
+@pytest.fixture(scope="session")
+def setuptools_sdist(tmp_path_factory, request):
+ with contexts.session_locked_tmp_dir(
+ request, tmp_path_factory, "sdist_build") as tmp:
+ dist = next(tmp.glob("*.tar.gz"), None)
+ if dist:
+ return dist
+
+ subprocess.check_call([
+ sys.executable, "-m", "build", "--sdist",
+ "--outdir", str(tmp), str(request.config.rootdir)
+ ])
+ return next(tmp.glob("*.tar.gz"))
+
+
+@pytest.fixture(scope="session")
+def setuptools_wheel(tmp_path_factory, request):
+ with contexts.session_locked_tmp_dir(
+ request, tmp_path_factory, "wheel_build") as tmp:
+ dist = next(tmp.glob("*.whl"), None)
+ if dist:
+ return dist
+
+ subprocess.check_call([
+ sys.executable, "-m", "build", "--wheel",
+ "--outdir", str(tmp) , str(request.config.rootdir)
+ ])
+ return next(tmp.glob("*.whl"))
+
+
+@pytest.fixture
+def venv(tmp_path, setuptools_wheel):
+ """Virtual env with the version of setuptools under test installed"""
+ env = environment.VirtualEnv()
+ env.root = path.Path(tmp_path / 'venv')
+ env.req = str(setuptools_wheel)
+ return env.create()
+
+
+@pytest.fixture
+def venv_without_setuptools(tmp_path):
+ """Virtual env without any version of setuptools installed"""
+ env = environment.VirtualEnv()
+ env.root = path.Path(tmp_path / 'venv_without_setuptools')
+ env.create_opts = ['--no-setuptools']
+ env.ensure_env()
+ return env
+
+
+@pytest.fixture
+def bare_venv(tmp_path):
+ """Virtual env without any common packages installed"""
+ env = environment.VirtualEnv()
+ env.root = path.Path(tmp_path / 'bare_venv')
+ env.create_opts = ['--no-setuptools', '--no-pip', '--no-wheel', '--no-seed']
+ env.ensure_env()
+ return env
diff --git a/setuptools/tests/integration/__init__.py b/setuptools/tests/integration/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/tests/integration/__init__.py
diff --git a/setuptools/tests/integration/helpers.py b/setuptools/tests/integration/helpers.py
new file mode 100644
index 00000000..43f43902
--- /dev/null
+++ b/setuptools/tests/integration/helpers.py
@@ -0,0 +1,61 @@
+"""Reusable functions and classes for different types of integration tests.
+
+For example ``Archive`` can be used to check the contents of distribution built
+with setuptools, and ``run`` will always try to be as verbose as possible to
+facilitate debugging.
+"""
+import os
+import subprocess
+import tarfile
+from zipfile import ZipFile
+
+
+def run(cmd, env=None):
+ r = subprocess.run(
+ cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ env={**os.environ, **(env or {})}
+ # ^-- allow overwriting instead of discarding the current env
+ )
+
+ out = r.stdout + "\n" + r.stderr
+ # pytest omits stdout/err by default, if the test fails they help debugging
+ print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
+ print(f"Command: {cmd}\nreturn code: {r.returncode}\n\n{out}")
+
+ if r.returncode == 0:
+ return out
+ raise subprocess.CalledProcessError(r.returncode, cmd, r.stdout, r.stderr)
+
+
+class Archive:
+ """Compatibility layer for ZipFile/Info and TarFile/Info"""
+ def __init__(self, filename):
+ self._filename = filename
+ if filename.endswith("tar.gz"):
+ self._obj = tarfile.open(filename, "r:gz")
+ elif filename.endswith("zip"):
+ self._obj = ZipFile(filename)
+ else:
+ raise ValueError(f"{filename} doesn't seem to be a zip or tar.gz")
+
+ def __iter__(self):
+ if hasattr(self._obj, "infolist"):
+ return iter(self._obj.infolist())
+ return iter(self._obj)
+
+ def get_name(self, zip_or_tar_info):
+ if hasattr(zip_or_tar_info, "filename"):
+ return zip_or_tar_info.filename
+ return zip_or_tar_info.name
+
+ def get_content(self, zip_or_tar_info):
+ if hasattr(self._obj, "extractfile"):
+ content = self._obj.extractfile(zip_or_tar_info)
+ if content is None:
+ msg = f"Invalid {zip_or_tar_info.name} in {self._filename}"
+ raise ValueError(msg)
+ return str(content.read(), "utf-8")
+ return str(self._obj.read(zip_or_tar_info), "utf-8")
diff --git a/setuptools/tests/integration/test_pip_install_sdist.py b/setuptools/tests/integration/test_pip_install_sdist.py
new file mode 100644
index 00000000..86cc4235
--- /dev/null
+++ b/setuptools/tests/integration/test_pip_install_sdist.py
@@ -0,0 +1,218 @@
+"""Integration tests for setuptools that focus on building packages via pip.
+
+The idea behind these tests is not to exhaustively check all the possible
+combinations of packages, operating systems, supporting libraries, etc, but
+rather check a limited number of popular packages and how they interact with
+the exposed public API. This way if any change in API is introduced, we hope to
+identify backward compatibility problems before publishing a release.
+
+The number of tested packages is purposefully kept small, to minimise duration
+and the associated maintenance cost (changes in the way these packages define
+their build process may require changes in the tests).
+"""
+import json
+import os
+import shutil
+import sys
+from enum import Enum
+from glob import glob
+from hashlib import md5
+from urllib.request import urlopen
+
+import pytest
+from packaging.requirements import Requirement
+
+from .helpers import Archive, run
+
+
+pytestmark = pytest.mark.integration
+
+LATEST, = list(Enum("v", "LATEST"))
+"""Default version to be checked"""
+# There are positive and negative aspects of checking the latest version of the
+# packages.
+# The main positive aspect is that the latest version might have already
+# removed the use of APIs deprecated in previous releases of setuptools.
+
+
+# Packages to be tested:
+# (Please notice the test environment cannot support EVERY library required for
+# compiling binary extensions. In Ubuntu/Debian nomenclature, we only assume
+# that `build-essential`, `gfortran` and `libopenblas-dev` are installed,
+# due to their relevance to the numerical/scientific programming ecosystem)
+EXAMPLES = [
+ ("pandas", LATEST), # cython + custom build_ext
+ ("sphinx", LATEST), # custom setup.py
+ ("pip", LATEST), # just in case...
+ ("pytest", LATEST), # uses setuptools_scm
+ ("mypy", LATEST), # custom build_py + ext_modules
+
+ # --- Popular packages: https://hugovk.github.io/top-pypi-packages/ ---
+ ("botocore", LATEST),
+ ("kiwisolver", "1.3.2"), # build_ext, version pinned due to setup_requires
+ ("brotli", LATEST), # not in the list but used by urllib3
+
+ # When adding packages to this list, make sure they expose a `__version__`
+ # attribute, or modify the tests bellow
+]
+
+
+# Some packages have "optional" dependencies that modify their build behaviour
+# and are not listed in pyproject.toml, others still use `setup_requires`
+EXTRA_BUILD_DEPS = {
+ "sphinx": ("babel>=1.3",),
+ "kiwisolver": ("cppy>=1.1.0",)
+}
+
+
+VIRTUALENV = (sys.executable, "-m", "virtualenv")
+
+
+# By default, pip will try to build packages in isolation (PEP 517), which
+# means it will download the previous stable version of setuptools.
+# `pip` flags can avoid that (the version of setuptools under test
+# should be the one to be used)
+SDIST_OPTIONS = (
+ "--ignore-installed",
+ "--no-build-isolation",
+ # We don't need "--no-binary :all:" since we specify the path to the sdist.
+ # It also helps with performance, since dependencies can come from wheels.
+)
+# The downside of `--no-build-isolation` is that pip will not download build
+# dependencies. The test script will have to also handle that.
+
+
+@pytest.fixture
+def venv_python(tmp_path):
+ run([*VIRTUALENV, str(tmp_path / ".venv")])
+ possible_path = (str(p.parent) for p in tmp_path.glob(".venv/*/python*"))
+ return shutil.which("python", path=os.pathsep.join(possible_path))
+
+
+@pytest.fixture(autouse=True)
+def _prepare(tmp_path, venv_python, monkeypatch, request):
+ download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path))
+ os.makedirs(download_path, exist_ok=True)
+
+ # Environment vars used for building some of the packages
+ monkeypatch.setenv("USE_MYPYC", "1")
+
+ def _debug_info():
+ # Let's provide the maximum amount of information possible in the case
+ # it is necessary to debug the tests directly from the CI logs.
+ print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
+ print("Temporary directory:")
+ map(print, tmp_path.glob("*"))
+ print("Virtual environment:")
+ run([venv_python, "-m", "pip", "freeze"])
+ request.addfinalizer(_debug_info)
+
+
+ALREADY_LOADED = ("pytest", "mypy") # loaded by pytest/pytest-enabler
+
+
+@pytest.mark.parametrize('package, version', EXAMPLES)
+def test_install_sdist(package, version, tmp_path, venv_python, setuptools_wheel):
+ venv_pip = (venv_python, "-m", "pip")
+ sdist = retrieve_sdist(package, version, tmp_path)
+ deps = build_deps(package, sdist)
+ if deps:
+ print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
+ print("Dependencies:", deps)
+ run([*venv_pip, "install", *deps])
+
+ # Use a virtualenv to simulate PEP 517 isolation
+ # but install fresh setuptools wheel to ensure the version under development
+ run([*venv_pip, "install", "-I", setuptools_wheel])
+ run([*venv_pip, "install", *SDIST_OPTIONS, sdist])
+
+ # Execute a simple script to make sure the package was installed correctly
+ script = f"import {package}; print(getattr({package}, '__version__', 0))"
+ run([venv_python, "-c", script])
+
+
+# ---- Helper Functions ----
+
+
+def retrieve_sdist(package, version, tmp_path):
+ """Either use cached sdist file or download it from PyPI"""
+ # `pip download` cannot be used due to
+ # https://github.com/pypa/pip/issues/1884
+ # https://discuss.python.org/t/pep-625-file-name-of-a-source-distribution/4686
+ # We have to find the correct distribution file and download it
+ download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path))
+ dist = retrieve_pypi_sdist_metadata(package, version)
+
+ # Remove old files to prevent cache to grow indefinitely
+ for file in glob(os.path.join(download_path, f"{package}*")):
+ if dist["filename"] != file:
+ os.unlink(file)
+
+ dist_file = os.path.join(download_path, dist["filename"])
+ if not os.path.exists(dist_file):
+ download(dist["url"], dist_file, dist["md5_digest"])
+ return dist_file
+
+
+def retrieve_pypi_sdist_metadata(package, version):
+ # https://warehouse.pypa.io/api-reference/json.html
+ id_ = package if version is LATEST else f"{package}/{version}"
+ with urlopen(f"https://pypi.org/pypi/{id_}/json") as f:
+ metadata = json.load(f)
+
+ if metadata["info"]["yanked"]:
+ raise ValueError(f"Release for {package} {version} was yanked")
+
+ version = metadata["info"]["version"]
+ release = metadata["releases"][version]
+ dists = [d for d in release if d["packagetype"] == "sdist"]
+ if len(dists) == 0:
+ raise ValueError(f"No sdist found for {package} {version}")
+
+ for dist in dists:
+ if dist["filename"].endswith(".tar.gz"):
+ return dist
+
+ # Not all packages are publishing tar.gz
+ return dist
+
+
+def download(url, dest, md5_digest):
+ with urlopen(url) as f:
+ data = f.read()
+
+ assert md5(data).hexdigest() == md5_digest
+
+ with open(dest, "wb") as f:
+ f.write(data)
+
+ assert os.path.exists(dest)
+
+
+def build_deps(package, sdist_file):
+ """Find out what are the build dependencies for a package.
+
+ We need to "manually" install them, since pip will not install build
+ deps with `--no-build-isolation`.
+ """
+ import tomli as toml
+
+ # delay importing, since pytest discovery phase may hit this file from a
+ # testenv without tomli
+
+ archive = Archive(sdist_file)
+ pyproject = _read_pyproject(archive)
+
+ info = toml.loads(pyproject)
+ deps = info.get("build-system", {}).get("requires", [])
+ deps += EXTRA_BUILD_DEPS.get(package, [])
+ # Remove setuptools from requirements (and deduplicate)
+ requirements = {Requirement(d).name: d for d in deps}
+ return [v for k, v in requirements.items() if k != "setuptools"]
+
+
+def _read_pyproject(archive):
+ for member in archive:
+ if os.path.basename(archive.get_name(member)) == "pyproject.toml":
+ return archive.get_content(member)
+ return ""
diff --git a/setuptools/tests/requirements.txt b/setuptools/tests/requirements.txt
deleted file mode 100644
index b2d84a94..00000000
--- a/setuptools/tests/requirements.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-mock
-pytest-flake8
-flake8-2020; python_version>="3.6"
-virtualenv>=13.0.0
-pytest-virtualenv>=1.2.7
-pytest>=3.7
-wheel
-coverage>=4.5.1
-pytest-cov>=2.5.1
-paver; python_version>="3.6"
-futures; python_version=="2.7"
-pip>=19.1 # For proper file:// URLs support.
-jaraco.envs
-sphinx
diff --git a/setuptools/tests/test_bdist_deprecations.py b/setuptools/tests/test_bdist_deprecations.py
index 28482fd0..1a900c67 100644
--- a/setuptools/tests/test_bdist_deprecations.py
+++ b/setuptools/tests/test_bdist_deprecations.py
@@ -11,7 +11,7 @@ from setuptools import SetuptoolsDeprecationWarning
@pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only')
@mock.patch('distutils.command.bdist_rpm.bdist_rpm')
-def test_bdist_rpm_warning(distutils_cmd):
+def test_bdist_rpm_warning(distutils_cmd, tmpdir_cwd):
dist = Distribution(
dict(
script_name='setup.py',
diff --git a/setuptools/tests/test_bdist_egg.py b/setuptools/tests/test_bdist_egg.py
index fb5b90b1..67f788cc 100644
--- a/setuptools/tests/test_bdist_egg.py
+++ b/setuptools/tests/test_bdist_egg.py
@@ -13,7 +13,7 @@ from . import contexts
SETUP_PY = """\
from setuptools import setup
-setup(name='foo', py_modules=['hi'])
+setup(py_modules=['hi'])
"""
@@ -52,7 +52,6 @@ class Test:
dist = Distribution(dict(
script_name='setup.py',
script_args=['bdist_egg', '--exclude-source-files'],
- name='foo',
py_modules=['hi'],
))
with contexts.quiet():
diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py
index 78a31ac4..19c8b780 100644
--- a/setuptools/tests/test_build_py.py
+++ b/setuptools/tests/test_build_py.py
@@ -18,7 +18,6 @@ def test_directories_in_package_data_glob(tmpdir_cwd):
script_name='setup.py',
script_args=['build_py'],
packages=[''],
- name='foo',
package_data={'': ['path/*']},
))
os.makedirs('path/subpath')
@@ -40,7 +39,6 @@ def test_read_only(tmpdir_cwd):
script_args=['build_py'],
packages=['pkg'],
package_data={'pkg': ['data.dat']},
- name='pkg',
))
os.makedirs('pkg')
open('pkg/__init__.py', 'w').close()
@@ -70,7 +68,6 @@ def test_executable_data(tmpdir_cwd):
script_args=['build_py'],
packages=['pkg'],
package_data={'pkg': ['run-me']},
- name='pkg',
))
os.makedirs('pkg')
open('pkg/__init__.py', 'w').close()
diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py
index 70c5794c..c52072ac 100644
--- a/setuptools/tests/test_develop.py
+++ b/setuptools/tests/test_develop.py
@@ -6,11 +6,11 @@ import sys
import subprocess
import platform
import pathlib
-import textwrap
from setuptools.command import test
import pytest
+import pip_run.launch
from setuptools.command.develop import develop
from setuptools.dist import Distribution
@@ -166,21 +166,6 @@ class TestNamespaces:
with test.test.paths_on_pythonpath([str(target)]):
subprocess.check_call(pkg_resources_imp)
- @staticmethod
- def install_workaround(site_packages):
- site_packages.mkdir(parents=True)
- sc = site_packages / 'sitecustomize.py'
- sc.write_text(
- textwrap.dedent(
- """
- import site
- import pathlib
- here = pathlib.Path(__file__).parent
- site.addsitedir(str(here))
- """
- ).lstrip()
- )
-
@pytest.mark.xfail(
platform.python_implementation() == 'PyPy',
reason="Workaround fails on PyPy (why?)",
@@ -190,7 +175,6 @@ class TestNamespaces:
Editable install to a prefix should be discoverable.
"""
prefix = tmp_path / 'prefix'
- prefix.mkdir()
# figure out where pip will likely install the package
site_packages = prefix / next(
@@ -198,9 +182,10 @@ class TestNamespaces:
for path in sys.path
if 'site-packages' in path and path.startswith(sys.prefix)
)
+ site_packages.mkdir(parents=True)
- # install the workaround
- self.install_workaround(site_packages)
+ # install workaround
+ pip_run.launch.inject_sitecustomize(str(site_packages))
env = dict(os.environ, PYTHONPATH=str(site_packages))
cmd = [
@@ -219,6 +204,6 @@ class TestNamespaces:
# now run 'sample' with the prefix on the PYTHONPATH
bin = 'Scripts' if platform.system() == 'Windows' else 'bin'
exe = prefix / bin / 'sample'
- if sys.version_info < (3, 7) and platform.system() == 'Windows':
+ if sys.version_info < (3, 8) and platform.system() == 'Windows':
exe = str(exe)
subprocess.check_call([exe], env=env)
diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py
index c4279f0b..4980f2c3 100644
--- a/setuptools/tests/test_dist.py
+++ b/setuptools/tests/test_dist.py
@@ -374,3 +374,8 @@ def test_check_specifier():
)
def test_rfc822_unescape(content, result):
assert (result or content) == rfc822_unescape(rfc822_escape(content))
+
+
+def test_metadata_name():
+ with pytest.raises(DistutilsSetupError, match='missing.*name'):
+ Distribution()._validate_metadata()
diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py
index b6b9c00e..366f2928 100644
--- a/setuptools/tests/test_distutils_adoption.py
+++ b/setuptools/tests/test_distutils_adoption.py
@@ -1,38 +1,15 @@
import os
import sys
import functools
-import subprocess
import platform
+import textwrap
import pytest
-import jaraco.envs
-import path
IS_PYPY = '__pypy__' in sys.builtin_module_names
-class VirtualEnv(jaraco.envs.VirtualEnv):
- name = '.env'
- # Some version of PyPy will import distutils on startup, implicitly
- # importing setuptools, and thus leading to BackendInvalid errors
- # when upgrading Setuptools. Bypass this behavior by avoiding the
- # early availability and need to upgrade.
- create_opts = ['--no-setuptools']
-
- def run(self, cmd, *args, **kwargs):
- cmd = [self.exe(cmd[0])] + cmd[1:]
- return subprocess.check_output(cmd, *args, cwd=self.root, **kwargs)
-
-
-@pytest.fixture
-def venv(tmp_path, tmp_src):
- env = VirtualEnv()
- env.root = path.Path(tmp_path / 'venv')
- env.req = str(tmp_src)
- return env.create()
-
-
def popen_text(call):
"""
Augment the Popen call with the parameters to ensure unicode text.
@@ -41,12 +18,35 @@ def popen_text(call):
if sys.version_info < (3, 7) else functools.partial(call, text=True)
+def win_sr(env):
+ """
+ On Windows, SYSTEMROOT must be present to avoid
+
+ > Fatal Python error: _Py_HashRandomization_Init: failed to
+ > get random numbers to initialize Python
+ """
+ if env is None:
+ return
+ if platform.system() == 'Windows':
+ env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
+ return env
+
+
def find_distutils(venv, imports='distutils', env=None, **kwargs):
py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals())
cmd = ['python', '-c', py_cmd]
- if platform.system() == 'Windows':
- env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
- return popen_text(venv.run)(cmd, env=env, **kwargs)
+ return popen_text(venv.run)(cmd, env=win_sr(env), **kwargs)
+
+
+def count_meta_path(venv, env=None):
+ py_cmd = textwrap.dedent(
+ """
+ import sys
+ is_distutils = lambda finder: finder.__class__.__name__ == "DistutilsMetaFinder"
+ print(len(list(filter(is_distutils, sys.meta_path))))
+ """)
+ cmd = ['python', '-c', py_cmd]
+ return int(popen_text(venv.run)(cmd, env=win_sr(env)))
def test_distutils_stdlib(venv):
@@ -55,6 +55,7 @@ def test_distutils_stdlib(venv):
"""
env = dict(SETUPTOOLS_USE_DISTUTILS='stdlib')
assert venv.name not in find_distutils(venv, env=env).split(os.sep)
+ assert count_meta_path(venv, env=env) == 0
def test_distutils_local_with_setuptools(venv):
@@ -64,6 +65,7 @@ def test_distutils_local_with_setuptools(venv):
env = dict(SETUPTOOLS_USE_DISTUTILS='local')
loc = find_distutils(venv, imports='setuptools, distutils', env=env)
assert venv.name in loc.split(os.sep)
+ assert count_meta_path(venv, env=env) <= 1
@pytest.mark.xfail('IS_PYPY', reason='pypy imports distutils on startup')
@@ -74,3 +76,20 @@ def test_distutils_local(venv):
"""
env = dict(SETUPTOOLS_USE_DISTUTILS='local')
assert venv.name in find_distutils(venv, env=env).split(os.sep)
+ assert count_meta_path(venv, env=env) <= 1
+
+
+def test_pip_import(venv):
+ """
+ Ensure pip can be imported.
+ Regression test for #3002.
+ """
+ cmd = ['python', '-c', 'import pip']
+ popen_text(venv.run)(cmd)
+
+
+def test_distutils_has_origin():
+ """
+ Distutils module spec should have an origin. #2990.
+ """
+ assert __import__('distutils').__spec__.origin
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index 6840d03b..5831b267 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -17,6 +17,8 @@ import time
import re
import subprocess
import pathlib
+import warnings
+from collections import namedtuple
import pytest
from jaraco import path
@@ -62,7 +64,7 @@ class FakeDist:
SETUP_PY = DALS("""
from setuptools import setup
- setup(name='foo')
+ setup()
""")
@@ -1058,3 +1060,52 @@ class TestWindowsScriptWriter:
hdr = hdr.rstrip('\n')
# header should not start with an escaped quote
assert not hdr.startswith('\\"')
+
+
+VersionStub = namedtuple("VersionStub", "major, minor, micro, releaselevel, serial")
+
+
+def test_use_correct_python_version_string(tmpdir, tmpdir_cwd, monkeypatch):
+ # In issue #3001, easy_install wrongly uses the `python3.1` directory
+ # when the interpreter is `python3.10` and the `--user` option is given.
+ # See pypa/setuptools#3001.
+ dist = Distribution()
+ cmd = dist.get_command_obj('easy_install')
+ cmd.args = ['ok']
+ cmd.optimize = 0
+ cmd.user = True
+ cmd.install_userbase = str(tmpdir)
+ cmd.install_usersite = None
+ install_cmd = dist.get_command_obj('install')
+ install_cmd.install_userbase = str(tmpdir)
+ install_cmd.install_usersite = None
+
+ with monkeypatch.context() as patch, warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ version = '3.10.1 (main, Dec 21 2021, 09:17:12) [GCC 10.2.1 20210110]'
+ info = VersionStub(3, 10, 1, "final", 0)
+ patch.setattr('site.ENABLE_USER_SITE', True)
+ patch.setattr('sys.version', version)
+ patch.setattr('sys.version_info', info)
+ patch.setattr(cmd, 'create_home_path', mock.Mock())
+ cmd.finalize_options()
+
+ name = "pypy" if hasattr(sys, 'pypy_version_info') else "python"
+ install_dir = cmd.install_dir.lower()
+
+ # In some platforms (e.g. Windows), install_dir is mostly determined
+ # via `sysconfig`, which define constants eagerly at module creation.
+ # This means that monkeypatching `sys.version` to emulate 3.10 for testing
+ # may have no effect.
+ # The safest test here is to rely on the fact that 3.1 is no longer
+ # supported/tested, and make sure that if 'python3.1' ever appears in the string
+ # it is followed by another digit (e.g. 'python3.10').
+ if re.search(name + r'3\.?1', install_dir):
+ assert re.search(name + r'3\.?1\d', install_dir)
+
+ # The following "variables" are used for interpolation in distutils
+ # installation schemes, so it should be fair to treat them as "semi-public",
+ # or at least public enough so we can have a test to make sure they are correct
+ assert cmd.config_vars['py_version'] == '3.10.1'
+ assert cmd.config_vars['py_version_short'] == '3.10'
+ assert cmd.config_vars['py_version_nodot'] == '310'
diff --git a/setuptools/tests/test_logging.py b/setuptools/tests/test_logging.py
new file mode 100644
index 00000000..a5ddd56d
--- /dev/null
+++ b/setuptools/tests/test_logging.py
@@ -0,0 +1,36 @@
+import logging
+
+import pytest
+
+
+setup_py = """\
+from setuptools import setup
+
+setup(
+ name="test_logging",
+ version="0.0"
+)
+"""
+
+
+@pytest.mark.parametrize(
+ "flag, expected_level", [("--dry-run", "INFO"), ("--verbose", "DEBUG")]
+)
+def test_verbosity_level(tmp_path, monkeypatch, flag, expected_level):
+ """Make sure the correct verbosity level is set (issue #3038)"""
+ import setuptools # noqa: Import setuptools to monkeypatch distutils
+ import distutils # <- load distutils after all the patches take place
+
+ logger = logging.Logger(__name__)
+ monkeypatch.setattr(logging, "root", logger)
+ unset_log_level = logger.getEffectiveLevel()
+ assert logging.getLevelName(unset_log_level) == "NOTSET"
+
+ setup_script = tmp_path / "setup.py"
+ setup_script.write_text(setup_py)
+ dist = distutils.core.run_setup(setup_script, stop_after="init")
+ dist.script_args = [flag, "sdist"]
+ dist.parse_command_line() # <- where the log level is set
+ log_level = logger.getEffectiveLevel()
+ log_level_name = logging.getLevelName(log_level)
+ assert log_level_name == expected_level
diff --git a/setuptools/tests/test_setuptools.py b/setuptools/tests/test_setuptools.py
index 42f8e18b..b97faf17 100644
--- a/setuptools/tests/test_setuptools.py
+++ b/setuptools/tests/test_setuptools.py
@@ -7,16 +7,23 @@ import distutils.cmd
from distutils.errors import DistutilsOptionError
from distutils.errors import DistutilsSetupError
from distutils.core import Extension
-from distutils.version import LooseVersion
+from zipfile import ZipFile
import pytest
+from setuptools.extern.packaging import version
+
import setuptools
import setuptools.dist
import setuptools.depends as dep
from setuptools.depends import Require
+@pytest.fixture(autouse=True)
+def isolated_dir(tmpdir_cwd):
+ yield
+
+
def makeSetup(**args):
"""Return distribution from 'setup(**args)', without executing commands"""
@@ -84,12 +91,12 @@ class TestDepends:
assert req.name == 'Json'
assert req.module == 'json'
- assert req.requested_version == '1.0.3'
+ assert req.requested_version == version.Version('1.0.3')
assert req.attribute == '__version__'
assert req.full_name() == 'Json-1.0.3'
from json import __version__
- assert req.get_version() == __version__
+ assert str(req.get_version()) == __version__
assert req.version_ok('1.0.9')
assert not req.version_ok('0.9.1')
assert not req.version_ok('unknown')
@@ -97,11 +104,6 @@ class TestDepends:
assert req.is_present()
assert req.is_current()
- req = Require('Json 3000', '03000', 'json', format=LooseVersion)
- assert req.is_present()
- assert not req.is_current()
- assert not req.version_ok('unknown')
-
req = Require('Do-what-I-mean', '1.0', 'd-w-i-m')
assert not req.is_present()
assert not req.is_current()
@@ -293,3 +295,11 @@ def test_findall_missing_symlink(tmpdir, can_symlink):
os.symlink('foo', 'bar')
found = list(setuptools.findall())
assert found == []
+
+
+def test_its_own_wheel_does_not_contain_tests(setuptools_wheel):
+ with ZipFile(setuptools_wheel) as zipfile:
+ contents = [f.replace(os.sep, '/') for f in zipfile.namelist()]
+
+ for member in contents:
+ assert '/tests/' not in member
diff --git a/setuptools/tests/test_sphinx_upload_docs.py b/setuptools/tests/test_sphinx_upload_docs.py
index cc5b8293..f24077fd 100644
--- a/setuptools/tests/test_sphinx_upload_docs.py
+++ b/setuptools/tests/test_sphinx_upload_docs.py
@@ -25,7 +25,6 @@ def sphinx_doc_sample_project(tmpdir_cwd):
class TestSphinxUploadDocs:
def test_sphinx_doc(self):
params = dict(
- name='foo',
packages=['test'],
)
dist = Distribution(params)
diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py
index 6bce8e20..530474d7 100644
--- a/setuptools/tests/test_test.py
+++ b/setuptools/tests/test_test.py
@@ -1,7 +1,5 @@
-from distutils import log
-import os
-
import pytest
+from jaraco import path
from setuptools.command.test import test
from setuptools.dist import Distribution
@@ -9,100 +7,34 @@ from setuptools.dist import Distribution
from .textwrap import DALS
-SETUP_PY = DALS(
- """
- from setuptools import setup
-
- setup(name='foo',
- packages=['name', 'name.space', 'name.space.tests'],
- namespace_packages=['name'],
- test_suite='name.space.tests.test_suite',
- )
- """
-)
-
-NS_INIT = DALS(
- """
- # -*- coding: Latin-1 -*-
- # Söme Arbiträry Ünicode to test Distribute Issüé 310
- try:
- __import__('pkg_resources').declare_namespace(__name__)
- except ImportError:
- from pkgutil import extend_path
- __path__ = extend_path(__path__, __name__)
- """
-)
-
-TEST_PY = DALS(
- """
- import unittest
-
- class TestTest(unittest.TestCase):
- def test_test(self):
- print "Foo" # Should fail under Python 3
-
- test_suite = unittest.makeSuite(TestTest)
- """
-)
-
-
-@pytest.fixture
-def sample_test(tmpdir_cwd):
- os.makedirs('name/space/tests')
-
- # setup.py
- with open('setup.py', 'wt') as f:
- f.write(SETUP_PY)
-
- # name/__init__.py
- with open('name/__init__.py', 'wb') as f:
- f.write(NS_INIT.encode('Latin-1'))
-
- # name/space/__init__.py
- with open('name/space/__init__.py', 'wt') as f:
- f.write('#empty\n')
-
- # name/space/tests/__init__.py
- with open('name/space/tests/__init__.py', 'wt') as f:
- f.write(TEST_PY)
-
-
-@pytest.fixture
-def quiet_log():
- # Running some of the other tests will automatically
- # change the log level to info, messing our output.
- log.set_verbosity(0)
-
-
-@pytest.mark.usefixtures('tmpdir_cwd', 'quiet_log')
+@pytest.mark.usefixtures('tmpdir_cwd')
def test_tests_are_run_once(capfd):
params = dict(
- name='foo',
packages=['dummy'],
)
- with open('setup.py', 'wt') as f:
- f.write('from setuptools import setup; setup(\n')
- for k, v in sorted(params.items()):
- f.write(' %s=%r,\n' % (k, v))
- f.write(')\n')
- os.makedirs('dummy')
- with open('dummy/__init__.py', 'wt'):
- pass
- with open('dummy/test_dummy.py', 'wt') as f:
- f.write(
- DALS(
+ files = {
+ 'setup.py':
+ 'from setuptools import setup; setup('
+ + ','.join(f'{name}={params[name]!r}' for name in params)
+ + ')',
+ 'dummy': {
+ '__init__.py': '',
+ 'test_dummy.py': DALS(
+ """
+ import unittest
+ class TestTest(unittest.TestCase):
+ def test_test(self):
+ print('Foo')
"""
- import unittest
- class TestTest(unittest.TestCase):
- def test_test(self):
- print('Foo')
- """
- )
- )
+ ),
+ },
+ }
+ path.build(files)
dist = Distribution(params)
dist.script_name = 'setup.py'
cmd = test(dist)
cmd.ensure_finalized()
cmd.run()
out, err = capfd.readouterr()
- assert out == 'Foo\n'
+ assert out.endswith('Foo\n')
+ assert len(out.split('Foo')) == 2
diff --git a/setuptools/tests/test_upload_docs.py b/setuptools/tests/test_upload_docs.py
index 55978aad..68977a5d 100644
--- a/setuptools/tests/test_upload_docs.py
+++ b/setuptools/tests/test_upload_docs.py
@@ -18,7 +18,7 @@ def sample_project(tmpdir_cwd):
'setup.py': DALS("""
from setuptools import setup
- setup(name='foo')
+ setup()
"""),
'build': {
'index.html': 'Hello world.',
diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py
index 00f5f185..0ba89643 100644
--- a/setuptools/tests/test_virtualenv.py
+++ b/setuptools/tests/test_virtualenv.py
@@ -1,52 +1,34 @@
-import glob
import os
import sys
import itertools
+import subprocess
import pathlib
import pytest
-from pytest_fixture_config import yield_requires_config
-
-import pytest_virtualenv
+from . import contexts
from .textwrap import DALS
from .test_easy_install import make_nspkg_sdist
@pytest.fixture(autouse=True)
-def pytest_virtualenv_works(virtualenv):
+def pytest_virtualenv_works(venv):
"""
pytest_virtualenv may not work. if it doesn't, skip these
tests. See #1284.
"""
- venv_prefix = virtualenv.run(
- 'python -c "import sys; print(sys.prefix)"',
- capture=True,
- ).strip()
+ venv_prefix = venv.run(["python" , "-c", "import sys; print(sys.prefix)"]).strip()
if venv_prefix == sys.prefix:
pytest.skip("virtualenv is broken (see pypa/setuptools#1284)")
-@yield_requires_config(pytest_virtualenv.CONFIG, ['virtualenv_executable'])
-@pytest.fixture(scope='function')
-def bare_virtualenv():
- """ Bare virtualenv (no pip/setuptools/wheel).
- """
- with pytest_virtualenv.VirtualEnv(args=(
- '--no-wheel',
- '--no-pip',
- '--no-setuptools',
- )) as venv:
- yield venv
-
-
-def test_clean_env_install(bare_virtualenv, tmp_src):
+def test_clean_env_install(venv_without_setuptools, setuptools_wheel):
"""
Check setuptools can be installed in a clean environment.
"""
- cmd = [bare_virtualenv.python, 'setup.py', 'install']
- bare_virtualenv.run(cmd, cd=tmp_src)
+ cmd = ["python", "-m", "pip", "install", str(setuptools_wheel)]
+ venv_without_setuptools.run(cmd)
def _get_pip_versions():
@@ -82,7 +64,7 @@ def _get_pip_versions():
'pip<22',
mark(
'https://github.com/pypa/pip/archive/main.zip',
- pytest.mark.skipif('sys.version_info < (3, 7)'),
+ pytest.mark.xfail(reason='#2975'),
),
]
@@ -99,41 +81,31 @@ def _get_pip_versions():
reason="https://github.com/pypa/setuptools/pull/2865#issuecomment-965834995",
)
@pytest.mark.parametrize('pip_version', _get_pip_versions())
-def test_pip_upgrade_from_source(pip_version, tmp_src, virtualenv):
+def test_pip_upgrade_from_source(pip_version, venv_without_setuptools,
+ setuptools_wheel, setuptools_sdist):
"""
Check pip can upgrade setuptools from source.
"""
- # Install pip/wheel, and remove setuptools (as it
+ # Install pip/wheel, in a venv without setuptools (as it
# should not be needed for bootstraping from source)
- if pip_version is None:
- upgrade_pip = ()
- else:
- upgrade_pip = ('python -m pip install -U "{pip_version}" --retries=1',)
- virtualenv.run(' && '.join((
- 'pip uninstall -y setuptools',
- 'pip install -U wheel',
- ) + upgrade_pip).format(pip_version=pip_version))
- dist_dir = virtualenv.workspace
- # Generate source distribution / wheel.
- virtualenv.run(' && '.join((
- 'python setup.py -q sdist -d {dist}',
- 'python setup.py -q bdist_wheel -d {dist}',
- )).format(dist=dist_dir), cd=tmp_src)
- sdist = glob.glob(os.path.join(dist_dir, '*.zip'))[0]
- wheel = glob.glob(os.path.join(dist_dir, '*.whl'))[0]
- # Then update from wheel.
- virtualenv.run('pip install ' + wheel)
+ venv = venv_without_setuptools
+ venv.run(["pip", "install", "-U", "wheel"])
+ if pip_version is not None:
+ venv.run(["python", "-m", "pip", "install", "-U", pip_version, "--retries=1"])
+ with pytest.raises(subprocess.CalledProcessError):
+ # Meta-test to make sure setuptools is not installed
+ venv.run(["python", "-c", "import setuptools"])
+
+ # Then install from wheel.
+ venv.run(["pip", "install", str(setuptools_wheel)])
# And finally try to upgrade from source.
- virtualenv.run('pip install --no-cache-dir --upgrade ' + sdist)
+ venv.run(["pip", "install", "--no-cache-dir", "--upgrade", str(setuptools_sdist)])
-def _check_test_command_install_requirements(virtualenv, tmpdir, cwd):
+def _check_test_command_install_requirements(venv, tmpdir):
"""
Check the test command will install all required dependencies.
"""
- # Install setuptools.
- virtualenv.run('python setup.py develop', cd=cwd)
-
def sdist(distname, version):
dist_path = tmpdir.join('%s-%s.tar.gz' % (distname, version))
make_nspkg_sdist(str(dist_path), distname, version)
@@ -182,28 +154,24 @@ def _check_test_command_install_requirements(virtualenv, tmpdir, cwd):
open('success', 'w').close()
'''))
- # Run test command for test package.
- # use 'virtualenv.python' as workaround for man-group/pytest-plugins#166
- cmd = [virtualenv.python, 'setup.py', 'test', '-s', 'test']
- virtualenv.run(cmd, cd=str(tmpdir))
+
+ cmd = ["python", 'setup.py', 'test', '-s', 'test']
+ venv.run(cmd, cwd=str(tmpdir))
assert tmpdir.join('success').check()
-def test_test_command_install_requirements(virtualenv, tmpdir, request):
+def test_test_command_install_requirements(venv, tmpdir, tmpdir_cwd):
# Ensure pip/wheel packages are installed.
- virtualenv.run(
- "python -c \"__import__('pkg_resources').require(['pip', 'wheel'])\"")
- # uninstall setuptools so that 'setup.py develop' works
- virtualenv.run("python -m pip uninstall -y setuptools")
+ venv.run(["python", "-c", "__import__('pkg_resources').require(['pip', 'wheel'])"])
# disable index URL so bits and bobs aren't requested from PyPI
- virtualenv.env['PIP_NO_INDEX'] = '1'
- _check_test_command_install_requirements(virtualenv, tmpdir, request.config.rootdir)
+ with contexts.environment(PYTHONPATH=None, PIP_NO_INDEX="1"):
+ _check_test_command_install_requirements(venv, tmpdir)
-def test_no_missing_dependencies(bare_virtualenv, request):
+def test_no_missing_dependencies(bare_venv, request):
"""
Quick and dirty test to ensure all external dependencies are vendored.
"""
+ setuptools_dir = request.config.rootdir
for command in ('upload',): # sorted(distutils.command.__all__):
- cmd = [bare_virtualenv.python, 'setup.py', command, '-h']
- bare_virtualenv.run(cmd, cd=request.config.rootdir)
+ bare_venv.run(['python', 'setup.py', command, '-h'], cwd=setuptools_dir)
diff --git a/setuptools/tests/test_wheel.py b/setuptools/tests/test_wheel.py
index 7345b135..a15c3a46 100644
--- a/setuptools/tests/test_wheel.py
+++ b/setuptools/tests/test_wheel.py
@@ -148,6 +148,7 @@ def _check_wheel_install(filename, install_dir, install_tree_includes,
if requires_txt is None:
assert not dist.has_metadata('requires.txt')
else:
+ # Order must match to ensure reproducibility.
assert requires_txt == dist.get_metadata('requires.txt').lstrip()
@@ -420,6 +421,38 @@ WHEEL_INSTALL_TESTS = (
),
dict(
+ id='requires_ensure_order',
+ install_requires='''
+ foo
+ bar
+ baz
+ qux
+ ''',
+ extras_require={
+ 'extra': '''
+ foobar>3
+ barbaz>4
+ bazqux>5
+ quxzap>6
+ ''',
+ },
+ requires_txt=DALS(
+ '''
+ foo
+ bar
+ baz
+ qux
+
+ [extra]
+ foobar>3
+ barbaz>4
+ bazqux>5
+ quxzap>6
+ '''
+ ),
+ ),
+
+ dict(
id='namespace_package',
file_defs={
'foo': {
diff --git a/setuptools/wheel.py b/setuptools/wheel.py
index 0be811af..9819e8b9 100644
--- a/setuptools/wheel.py
+++ b/setuptools/wheel.py
@@ -136,13 +136,13 @@ class Wheel:
def raw_req(req):
req.marker = None
return str(req)
- install_requires = list(sorted(map(raw_req, dist.requires())))
+ install_requires = list(map(raw_req, dist.requires()))
extras_require = {
- extra: sorted(
+ 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)
diff --git a/tools/finalize.py b/tools/finalize.py
index 516a2fb5..e4f65543 100644
--- a/tools/finalize.py
+++ b/tools/finalize.py
@@ -79,11 +79,18 @@ def check_changes():
"""
allowed = 'deprecation', 'breaking', 'change', 'doc', 'misc'
except_ = 'README.rst', '.gitignore'
- assert all(
- any(key in file.name for key in allowed)
+ news_fragments = (
+ file
for file in pathlib.Path('changelog.d').iterdir()
if file.name not in except_
)
+ unrecognized = [
+ str(file)
+ for file in news_fragments
+ if not any(f".{key}" in file.suffixes for key in allowed)
+ ]
+ if unrecognized:
+ raise ValueError(f"Some news fragments have invalid names: {unrecognized}")
if __name__ == '__main__':
diff --git a/msvc-build-launcher-arm64.cmd b/tools/msvc-build-launcher-arm64.cmd
index 8e63506b..8e63506b 100644
--- a/msvc-build-launcher-arm64.cmd
+++ b/tools/msvc-build-launcher-arm64.cmd
diff --git a/msvc-build-launcher.cmd b/tools/msvc-build-launcher.cmd
index 92da290e..92da290e 100644
--- a/msvc-build-launcher.cmd
+++ b/tools/msvc-build-launcher.cmd
diff --git a/towncrier_template.rst b/tools/towncrier_template.rst
index 7f507342..7f507342 100644
--- a/towncrier_template.rst
+++ b/tools/towncrier_template.rst
diff --git a/tools/vendored.py b/tools/vendored.py
new file mode 100644
index 00000000..ee978e5c
--- /dev/null
+++ b/tools/vendored.py
@@ -0,0 +1,127 @@
+import re
+import sys
+import subprocess
+
+from path import Path
+
+
+def remove_all(paths):
+ for path in paths:
+ path.rmtree() if path.isdir() else path.remove()
+
+
+def update_vendored():
+ update_pkg_resources()
+ update_setuptools()
+
+
+def rewrite_packaging(pkg_files, new_root):
+ """
+ Rewrite imports in packaging to redirect to vendored copies.
+ """
+ for file in pkg_files.glob('*.py'):
+ text = file.text()
+ text = re.sub(r' (pyparsing)', rf' {new_root}.\1', text)
+ text = text.replace(
+ 'from six.moves.urllib import parse',
+ 'from urllib import parse',
+ )
+ file.write_text(text)
+
+
+def rewrite_jaraco_text(pkg_files, new_root):
+ """
+ Rewrite imports in jaraco.text to redirect to vendored copies.
+ """
+ for file in pkg_files.glob('*.py'):
+ text = file.read_text()
+ text = re.sub(r' (jaraco\.)', rf' {new_root}.\1', text)
+ text = re.sub(r' (importlib_resources)', rf' {new_root}.\1', text)
+ # suppress loading of lorem_ipsum; ref #3072
+ text = re.sub(r'^lorem_ipsum.*\n$', '', text, flags=re.M)
+ file.write_text(text)
+
+
+def rewrite_jaraco(pkg_files, new_root):
+ """
+ Rewrite imports in jaraco.functools to redirect to vendored copies.
+ """
+ for file in pkg_files.glob('*.py'):
+ text = file.read_text()
+ text = re.sub(r' (more_itertools)', rf' {new_root}.\1', text)
+ file.write_text(text)
+ # required for zip-packaged setuptools #3084
+ pkg_files.joinpath('__init__.py').write_text('')
+
+
+def rewrite_importlib_resources(pkg_files, new_root):
+ """
+ Rewrite imports in importlib_resources to redirect to vendored copies.
+ """
+ for file in pkg_files.glob('*.py'):
+ text = file.read_text().replace('importlib_resources.abc', '.abc')
+ text = text.replace('zipp', '..zipp')
+ file.write_text(text)
+
+
+def rewrite_more_itertools(pkg_files: Path):
+ """
+ Defer import of concurrent.futures. Workaround for #3090.
+ """
+ more_file = pkg_files.joinpath('more.py')
+ text = more_file.read_text()
+ text = re.sub(r'^.*concurrent.futures.*?\n', '', text, flags=re.MULTILINE)
+ text = re.sub(
+ 'ThreadPoolExecutor',
+ '__import__("concurrent.futures").futures.ThreadPoolExecutor',
+ text,
+ )
+ more_file.write_text(text)
+
+
+def clean(vendor):
+ """
+ Remove all files out of the vendor directory except the meta
+ data (as pip uninstall doesn't support -t).
+ """
+ remove_all(
+ path
+ for path in vendor.glob('*')
+ if path.basename() != 'vendored.txt'
+ )
+
+
+def install(vendor):
+ clean(vendor)
+ install_args = [
+ sys.executable,
+ '-m', 'pip',
+ 'install',
+ '-r', str(vendor / 'vendored.txt'),
+ '-t', str(vendor),
+ ]
+ subprocess.check_call(install_args)
+ (vendor / '__init__.py').write_text('')
+
+
+def update_pkg_resources():
+ vendor = Path('pkg_resources/_vendor')
+ install(vendor)
+ rewrite_packaging(vendor / 'packaging', 'pkg_resources.extern')
+ rewrite_jaraco_text(vendor / 'jaraco/text', 'pkg_resources.extern')
+ rewrite_jaraco(vendor / 'jaraco', 'pkg_resources.extern')
+ rewrite_importlib_resources(vendor / 'importlib_resources', 'pkg_resources.extern')
+ rewrite_more_itertools(vendor / "more_itertools")
+
+
+def update_setuptools():
+ vendor = Path('setuptools/_vendor')
+ install(vendor)
+ rewrite_packaging(vendor / 'packaging', 'setuptools.extern')
+ rewrite_jaraco_text(vendor / 'jaraco/text', 'setuptools.extern')
+ rewrite_jaraco(vendor / 'jaraco', 'setuptools.extern')
+ rewrite_importlib_resources(vendor / 'importlib_resources', 'setuptools.extern')
+ rewrite_more_itertools(vendor / "more_itertools")
+
+
+__name__ == '__main__' and update_vendored()
diff --git a/tox.ini b/tox.ini
index 25b4eaf0..26aefada 100644
--- a/tox.ini
+++ b/tox.ini
@@ -7,11 +7,7 @@ toxworkdir={env:TOX_WORK_DIR:.tox}
[testenv]
deps =
- # workaround for sphinx-doc/sphinx#9562
- # TODO: remove after Sphinx>4.1.2 is available.
- sphinx@git+https://github.com/sphinx-doc/sphinx; python_version>="3.10"
- # TODO: remove after man-group/pytest-plugins#188 is solved
- pytest-virtualenv @ git+https://github.com/jaraco/pytest-plugins@distutils-deprecated#subdirectory=pytest-virtualenv
+ # Ideally all the dependencies should be set as "extras"
commands =
pytest {posargs}
usedevelop = True
@@ -19,6 +15,20 @@ extras = testing
passenv =
SETUPTOOLS_USE_DISTUTILS
windir # required for test_pkg_resources
+ # honor git config in pytest-perf
+ HOME
+
+[testenv:integration]
+deps = {[testenv]deps}
+extras = testing-integration
+passenv =
+ {[testenv]passenv}
+ DOWNLOAD_PATH
+setenv =
+ PROJECT_ROOT = {toxinidir}
+commands =
+ pytest --integration {posargs:-vv --durations=10 setuptools/tests/integration}
+ # use verbose mode by default to facilitate debugging from CI logs
[testenv:docs]
extras =
@@ -37,6 +47,13 @@ passenv = *
commands =
python tools/finalize.py
+[testenv:vendor]
+skip_install = True
+deps =
+ path
+commands =
+ python -m tools.vendored
+
[testenv:release]
skip_install = True
deps =