From fb90dc349e7434f0603a17176b48588ca74b870e Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 6 Mar 2024 14:32:58 -0500 Subject: [PATCH 001/135] Runtime fixes to make typing annotations work --- pkg_resources/__init__.py | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 163a5521d6..002ea4d2f9 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -27,7 +27,7 @@ import time import re import types -from typing import List, Protocol +from typing import Dict, List, Protocol import zipfile import zipimport import warnings @@ -920,10 +920,10 @@ def find_plugins(self, plugin_env, full_env=None, installer=None, fallback=True) # success, no need to try any more versions of this project break - distributions = list(distributions) - distributions.sort() + sorted_distributions = list(distributions) + sorted_distributions.sort() - return distributions, error_info + return sorted_distributions, error_info def require(self, *requirements): """Ensure that distributions matching `requirements` are activated @@ -1636,7 +1636,7 @@ def _validate_resource_path(path): ) def _get(self, path) -> bytes: - if hasattr(self.loader, 'get_data'): + if self.loader and hasattr(self.loader, 'get_data'): return self.loader.get_data(path) raise NotImplementedError( "Can't perform this operation for loaders without 'get_data()'" @@ -2492,7 +2492,7 @@ def resolve(self): raise ImportError(str(exc)) from exc def require(self, env=None, installer=None): - if self.extras and not self.dist: + if not self.dist: raise UnknownExtra("Can't require() without a distribution", self) # Get the requirements for this entry point with all its extras and @@ -2559,11 +2559,11 @@ def parse_group(cls, group, lines, dist=None): def parse_map(cls, data, dist=None): """Parse a map of entry point groups""" if isinstance(data, dict): - data = data.items() + _data = data.items() else: - data = split_sections(data) - maps = {} - for group, lines in data: + _data = split_sections(data) + maps: Dict[str, Dict[str, "EntryPoint"]] = {} + for group, lines in _data: if group is None: if not lines: continue @@ -2825,7 +2825,7 @@ def activate(self, path=None, replace=False): if path is None: path = sys.path self.insert_on(path, replace=replace) - if path is sys.path: + if path is sys.path and self.location: fixup_namespace_packages(self.location) for pkg in self._get_metadata('namespace_packages.txt'): if pkg in sys.modules: @@ -2893,15 +2893,13 @@ def load_entry_point(self, group, name): def get_entry_map(self, group=None): """Return the entry point map for `group`, or the full entry map""" - try: - ep_map = self._ep_map - except AttributeError: - ep_map = self._ep_map = EntryPoint.parse_map( + if not hasattr(self, "_ep_map"): + self._ep_map = EntryPoint.parse_map( self._get_metadata('entry_points.txt'), self ) if group is not None: - return ep_map.get(group, {}) - return ep_map + return self._ep_map.get(group, {}) + return self._ep_map def get_entry_info(self, group, name): """Return the EntryPoint object for `group`+`name`, or ``None``""" From b0a4c08f32d3087197331db257919be9355196ef Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 7 Mar 2024 12:35:23 -0500 Subject: [PATCH 002/135] Avoid mypy note --- pkg_resources/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 002ea4d2f9..95bc9a6006 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -27,7 +27,7 @@ import time import re import types -from typing import Dict, List, Protocol +from typing import List, Protocol import zipfile import zipimport import warnings @@ -2562,7 +2562,7 @@ def parse_map(cls, data, dist=None): _data = data.items() else: _data = split_sections(data) - maps: Dict[str, Dict[str, "EntryPoint"]] = {} + maps = {} for group, lines in _data: if group is None: if not lines: From 2bb4cc0d83d441f8c06a7b6b7989ba1891683e20 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 8 Mar 2024 14:57:06 -0500 Subject: [PATCH 003/135] Updates from PR Review --- newsfragments/4262.feature.rst | 3 +++ pkg_resources/__init__.py | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 newsfragments/4262.feature.rst diff --git a/newsfragments/4262.feature.rst b/newsfragments/4262.feature.rst new file mode 100644 index 0000000000..7bbdba87d2 --- /dev/null +++ b/newsfragments/4262.feature.rst @@ -0,0 +1,3 @@ +Improved `AttributeError` error message if ``pkg_resources.EntryPoint.require`` is called without extras or distribution +Gracefully "do nothing" when trying to activate a ``pkg_resources.Distribution`` with a `None` location, rather than raising a `TypeError` +-- by :user:`Avasam` diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 95bc9a6006..67e26bb001 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1636,7 +1636,7 @@ def _validate_resource_path(path): ) def _get(self, path) -> bytes: - if self.loader and hasattr(self.loader, 'get_data'): + if hasattr(self.loader, 'get_data') and self.loader: return self.loader.get_data(path) raise NotImplementedError( "Can't perform this operation for loaders without 'get_data()'" @@ -2493,7 +2493,8 @@ def resolve(self): def require(self, env=None, installer=None): if not self.dist: - raise UnknownExtra("Can't require() without a distribution", self) + error_cls = UnknownExtra if self.extras else AttributeError + raise error_cls("Can't require() without a distribution", self) # Get the requirements for this entry point with all its extras and # then resolve them. We have to pass `extras` along when resolving so @@ -2825,7 +2826,7 @@ def activate(self, path=None, replace=False): if path is None: path = sys.path self.insert_on(path, replace=replace) - if path is sys.path and self.location: + if path is sys.path: fixup_namespace_packages(self.location) for pkg in self._get_metadata('namespace_packages.txt'): if pkg in sys.modules: From 2b339b98bcc26fe9147647054c8fa09344f581ec Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 15 Mar 2024 00:44:48 -0400 Subject: [PATCH 004/135] Avoid leaking "name" variable in AbstractSandbox --- newsfragments/4280.misc.rst | 1 + setuptools/sandbox.py | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) create mode 100644 newsfragments/4280.misc.rst diff --git a/newsfragments/4280.misc.rst b/newsfragments/4280.misc.rst new file mode 100644 index 0000000000..aff6a7ca1c --- /dev/null +++ b/newsfragments/4280.misc.rst @@ -0,0 +1 @@ +Avoid leaking loop variable ``name`` in ``AbstractSandbox`` -- by :user:`Avasam` diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 6c095e029e..e5da9d86f0 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -309,9 +309,9 @@ def wrap(self, src, dst, *args, **kw): return wrap - for name in ["rename", "link", "symlink"]: - if hasattr(_os, name): - locals()[name] = _mk_dual_path_wrapper(name) + for __name in ["rename", "link", "symlink"]: + if hasattr(_os, __name): + locals()[__name] = _mk_dual_path_wrapper(__name) def _mk_single_path_wrapper(name: str, original=None): # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099 original = original or getattr(_os, name) @@ -326,7 +326,7 @@ def wrap(self, path, *args, **kw): if _file: _file = _mk_single_path_wrapper('file', _file) _open = _mk_single_path_wrapper('open', _open) - for name in [ + for __name in [ "stat", "listdir", "chdir", @@ -347,8 +347,8 @@ def wrap(self, path, *args, **kw): "pathconf", "access", ]: - if hasattr(_os, name): - locals()[name] = _mk_single_path_wrapper(name) + if hasattr(_os, __name): + locals()[__name] = _mk_single_path_wrapper(__name) def _mk_single_with_return(name: str): # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099 original = getattr(_os, name) @@ -361,9 +361,9 @@ def wrap(self, path, *args, **kw): return wrap - for name in ['readlink', 'tempnam']: - if hasattr(_os, name): - locals()[name] = _mk_single_with_return(name) + for __name in ['readlink', 'tempnam']: + if hasattr(_os, __name): + locals()[__name] = _mk_single_with_return(__name) def _mk_query(name: str): # type: ignore[misc] # https://github.com/pypa/setuptools/pull/4099 original = getattr(_os, name) @@ -376,9 +376,9 @@ def wrap(self, *args, **kw): return wrap - for name in ['getcwd', 'tmpnam']: - if hasattr(_os, name): - locals()[name] = _mk_query(name) + for __name in ['getcwd', 'tmpnam']: + if hasattr(_os, __name): + locals()[__name] = _mk_query(__name) def _validate_path(self, path): """Called to remap or validate any path, whether input or output""" From d377ff738350743ce5e134e04031707605ec3dd3 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sat, 16 Mar 2024 02:20:46 -0400 Subject: [PATCH 005/135] Update codecov/codecov-action to v4 --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 87b7317f13..76178067b8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -100,7 +100,7 @@ jobs: run: pipx run coverage xml --ignore-errors - name: Publish coverage if: hashFiles('coverage.xml') != '' # Rudimentary `file.exists()` - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: flags: >- # Mark which lines are covered by which envs CI-GHA, @@ -108,6 +108,7 @@ jobs: OS-${{ runner.os }}, VM-${{ matrix.platform }}, Py-${{ steps.python-install.outputs.python-version }} + token: ${{ secrets.CODECOV_TOKEN }} collateral: strategy: @@ -190,7 +191,7 @@ jobs: shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} - name: Publish coverage if: hashFiles('coverage.xml') != '' # Rudimentary `file.exists()` - uses: codecov/codecov-action@v3 + uses: codecov/codecov-action@v4 with: files: >- ${{ github.workspace }}\coverage.xml @@ -200,6 +201,7 @@ jobs: OS-${{ runner.os }}, VM-${{ matrix.platform }}, Py-${{ steps.python-install.outputs.python-version }} + token: ${{ secrets.CODECOV_TOKEN }} integration-test: needs: test From 486a8afea286d4d67e5038b58bf4452ccfeebd69 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com> Date: Sun, 14 Apr 2024 03:06:34 -0600 Subject: [PATCH 006/135] NEWS: Put releases in numerical order --- NEWS.rst | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/NEWS.rst b/NEWS.rst index 08e28ecc28..9bf82560cc 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -4,23 +4,23 @@ v69.5.1 No significant changes. -v69.4.2 +v69.5.0 ======= -Bugfixes +Features -------- -- Merged bugfix for pypa/distutils#246 (#27489545) +- Refresh unpinned vendored dependencies. (#4253) +- Updated vendored packaging to version 24.0. (#4301) -v69.5.0 +v69.4.2 ======= -Features +Bugfixes -------- -- Refresh unpinned vendored dependencies. (#4253) -- Updated vendored packaging to version 24.0. (#4301) +- Merged bugfix for pypa/distutils#246 (#27489545) v69.4.1 @@ -29,22 +29,22 @@ v69.4.1 No significant changes. -v69.3.1 +v69.4.0 ======= -Bugfixes +Features -------- -- Remove attempt to canonicalize the version. It's already canonical enough. (#4302) +- Merged with pypa/distutils@55982565e, including interoperability improvements for rfc822_escape (pypa/distutils#213), dynamic resolution of config_h_filename for Python 3.13 compatibility (pypa/distutils#219), added support for the z/OS compiler (pypa/distutils#216), modernized compiler options in unixcompiler (pypa/distutils#214), fixed accumulating flags bug after compile/link (pypa/distutils#207), fixed enconding warnings (pypa/distutils#236), and general quality improvements (pypa/distutils#234). (#4298) -v69.4.0 +v69.3.1 ======= -Features +Bugfixes -------- -- Merged with pypa/distutils@55982565e, including interoperability improvements for rfc822_escape (pypa/distutils#213), dynamic resolution of config_h_filename for Python 3.13 compatibility (pypa/distutils#219), added support for the z/OS compiler (pypa/distutils#216), modernized compiler options in unixcompiler (pypa/distutils#214), fixed accumulating flags bug after compile/link (pypa/distutils#207), fixed enconding warnings (pypa/distutils#236), and general quality improvements (pypa/distutils#234). (#4298) +- Remove attempt to canonicalize the version. It's already canonical enough. (#4302) v69.3.0 From 9698925c4f6d5cf59d182dcc73682a07cb16b924 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sat, 16 Mar 2024 12:16:34 -0400 Subject: [PATCH 007/135] Deduplicate testing dependencies by dropping testing-integration --- newsfragments/4282.misc.rst | 1 + setup.cfg | 15 +-------------- tox.ini | 2 +- 3 files changed, 3 insertions(+), 15 deletions(-) create mode 100644 newsfragments/4282.misc.rst diff --git a/newsfragments/4282.misc.rst b/newsfragments/4282.misc.rst new file mode 100644 index 0000000000..841d1b292c --- /dev/null +++ b/newsfragments/4282.misc.rst @@ -0,0 +1 @@ +Removed the ``setuptools[testing-integration]`` in favor of ``setuptools[testing]`` -- by :user:`Avasam` diff --git a/setup.cfg b/setup.cfg index f7479e047f..214964fa98 100644 --- a/setup.cfg +++ b/setup.cfg @@ -60,7 +60,7 @@ testing = jaraco.envs>=2.2 pytest-xdist>=3 # Dropped dependency on pytest-fork and py jaraco.path>=3.2.0 - build[virtualenv] + build[virtualenv]>=1.0.3 filelock>=3.4.0 ini2toml[lite]>=0.9 tomli-w>=1.0.0 @@ -77,19 +77,6 @@ testing = # No Python 3.12 dependencies require importlib_metadata, but needed for type-checking since we import it directly importlib_metadata -testing-integration = - pytest - pytest-xdist - pytest-enabler - virtualenv>=13.0.0 - tomli - wheel - jaraco.path>=3.2.0 - jaraco.envs>=2.2 - build[virtualenv]>=1.0.3 - filelock>=3.4.0 - packaging>=23.2 - docs = # upstream sphinx >= 3.5 diff --git a/tox.ini b/tox.ini index 8815a697ab..fa4864f27b 100644 --- a/tox.ini +++ b/tox.ini @@ -23,7 +23,7 @@ pass_env = [testenv:integration] deps = {[testenv]deps} -extras = testing-integration +extras = testing pass_env = {[testenv]pass_env} DOWNLOAD_PATH From 3b6781d1d980d7ce16caacf3310c9f418b1feb56 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 20 Mar 2024 15:40:32 +0000 Subject: [PATCH 008/135] Update tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index fa4864f27b..7412730008 100644 --- a/tox.ini +++ b/tox.ini @@ -23,7 +23,7 @@ pass_env = [testenv:integration] deps = {[testenv]deps} -extras = testing +extras = {[testenv]extras} pass_env = {[testenv]pass_env} DOWNLOAD_PATH From 3ca45b7374ca8262b71e8197aba28f5580ab9550 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 20 Mar 2024 09:19:25 -0700 Subject: [PATCH 009/135] Ignore 'import-not-found' for _validate_pyproject --- mypy.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mypy.ini b/mypy.ini index ee12ebb193..45671826b1 100644 --- a/mypy.ini +++ b/mypy.ini @@ -32,5 +32,5 @@ ignore_missing_imports = True # - pkg_resources tests create modules that won't exists statically before the test is run. # Let's ignore all "import-not-found" since, if an import really wasn't found, then the test would fail. # - setuptools._vendor.packaging._manylinux: Mypy issue, this vendored module is already excluded! -[mypy-pkg_resources.tests.*,setuptools._vendor.packaging._manylinux] +[mypy-pkg_resources.tests.*,setuptools._vendor.packaging._manylinux,setuptools.config._validate_pyproject.*] disable_error_code = import-not-found From 6c118beac827d233e0d5af76a1555092f631ce70 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 16 Apr 2024 12:02:47 +0100 Subject: [PATCH 010/135] Disable plugins that already run on normal tests when running integration --- conftest.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/conftest.py b/conftest.py index 90b653146f..328d45d351 100644 --- a/conftest.py +++ b/conftest.py @@ -24,6 +24,7 @@ def pytest_addoption(parser): def pytest_configure(config): config.addinivalue_line("markers", "integration: integration tests") config.addinivalue_line("markers", "uses_network: tests may try to download files") + _IntegrationTestSpeedups.disable_plugins_already_run(config) collect_ignore = [ @@ -47,9 +48,25 @@ def pytest_configure(config): @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") + _IntegrationTestSpeedups.conditional_skip(request) + + +class _IntegrationTestSpeedups: + """Speed-up integration tests by only running what does not run in other tests.""" + + RUNS_ON_NORMAL_TESTS = ("checkdocks", "cov", "mypy", "perf", "ruff") + + @classmethod + def disable_plugins_already_run(cls, config): + if config.getoption("--integration"): + for plugin in cls.RUNS_ON_NORMAL_TESTS: # no need to run again + config.pluginmanager.set_blocked(plugin) + + @staticmethod + def conditional_skip(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") From 0e98638760ca714fef90b2cb0f361024e9ec570c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sviatoslav=20Sydorenko=20=28=D0=A1=D0=B2=D1=8F=D1=82=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B0=D0=B2=20=D0=A1=D0=B8=D0=B4=D0=BE=D1=80=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE=29?= Date: Tue, 16 Apr 2024 17:44:37 +0200 Subject: [PATCH 011/135] Fix the 608de826 commit reference in changelog Previously, the orphaned filename was used, making the current Sphinx setup link a non-existing issue. --- NEWS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.rst b/NEWS.rst index 9bf82560cc..a0714ab7d2 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -20,7 +20,7 @@ v69.4.2 Bugfixes -------- -- Merged bugfix for pypa/distutils#246 (#27489545) +- Merged bugfix for pypa/distutils#246 (pypa/setuptools@608de826) v69.4.1 From 6067fa41a3d1dfa8255ce571c9789153bb03630a Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 16 Apr 2024 14:14:14 -0400 Subject: [PATCH 012/135] Removed meaningless reference from 69.4.2 release notes. --- NEWS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.rst b/NEWS.rst index a0714ab7d2..20c6903a33 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -20,7 +20,7 @@ v69.4.2 Bugfixes -------- -- Merged bugfix for pypa/distutils#246 (pypa/setuptools@608de826) +- Merged bugfix for pypa/distutils#246. v69.4.1 From 42d1b3ceaaeeaa01eef8ad1e3a883a5324c6275b Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Fri, 19 Apr 2024 06:33:06 -0400 Subject: [PATCH 013/135] Mark tests as xfail. All are marked as xfail even though only two are failing. As far as I know, there's no easy way to mark some of the parameterized tests as xfail without splitting and selecting, so just get the whole bunch. Ref #4315 --- setuptools/tests/config/test_apply_pyprojecttoml.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setuptools/tests/config/test_apply_pyprojecttoml.py b/setuptools/tests/config/test_apply_pyprojecttoml.py index 2ca35759bc..27e57b27c7 100644 --- a/setuptools/tests/config/test_apply_pyprojecttoml.py +++ b/setuptools/tests/config/test_apply_pyprojecttoml.py @@ -37,6 +37,7 @@ def makedist(path, **attrs): return Distribution({"src_root": path, **attrs}) +@pytest.mark.xfail(reason="#4315") @pytest.mark.parametrize("url", urls_from_file(HERE / EXAMPLES_FILE)) @pytest.mark.filterwarnings("ignore") @pytest.mark.uses_network From 12ab7d85b74ce299d983e29cb4caff68818731dd Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sat, 20 Apr 2024 20:20:03 +0100 Subject: [PATCH 014/135] Make test_apply_pyprojecttoml more deterministic with new version of ini2toml --- setup.cfg | 2 +- setuptools/tests/config/test_apply_pyprojecttoml.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 214964fa98..c8bb0ed41d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -62,7 +62,7 @@ testing = jaraco.path>=3.2.0 build[virtualenv]>=1.0.3 filelock>=3.4.0 - ini2toml[lite]>=0.9 + ini2toml[lite]>=0.14 tomli-w>=1.0.0 pytest-timeout pytest-perf; \ diff --git a/setuptools/tests/config/test_apply_pyprojecttoml.py b/setuptools/tests/config/test_apply_pyprojecttoml.py index 27e57b27c7..1b9fd6b683 100644 --- a/setuptools/tests/config/test_apply_pyprojecttoml.py +++ b/setuptools/tests/config/test_apply_pyprojecttoml.py @@ -14,7 +14,7 @@ from zipfile import ZipFile import pytest -from ini2toml.api import Translator +from ini2toml.api import LiteTranslator from packaging.metadata import Metadata @@ -46,7 +46,7 @@ def test_apply_pyproject_equivalent_to_setupcfg(url, monkeypatch, tmp_path): setupcfg_example = retrieve_file(url) pyproject_example = Path(tmp_path, "pyproject.toml") setupcfg_text = setupcfg_example.read_text(encoding="utf-8") - toml_config = Translator().translate(setupcfg_text, "setup.cfg") + toml_config = LiteTranslator().translate(setupcfg_text, "setup.cfg") pyproject_example.write_text(toml_config, encoding="utf-8") dist_toml = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject_example) From fb6dc4de0ca47dc4356dc25ac63ea187f2ad8f5a Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sat, 20 Apr 2024 20:26:59 +0100 Subject: [PATCH 015/135] Remove solved xfail in test_apply_pyproject_equivalent_to_setupcfg --- setuptools/tests/config/test_apply_pyprojecttoml.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setuptools/tests/config/test_apply_pyprojecttoml.py b/setuptools/tests/config/test_apply_pyprojecttoml.py index 1b9fd6b683..bb78f64310 100644 --- a/setuptools/tests/config/test_apply_pyprojecttoml.py +++ b/setuptools/tests/config/test_apply_pyprojecttoml.py @@ -37,7 +37,6 @@ def makedist(path, **attrs): return Distribution({"src_root": path, **attrs}) -@pytest.mark.xfail(reason="#4315") @pytest.mark.parametrize("url", urls_from_file(HERE / EXAMPLES_FILE)) @pytest.mark.filterwarnings("ignore") @pytest.mark.uses_network From 8837459281c144cd5bbec7a43aaf5e0a727c0efa Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 16 Apr 2024 19:52:26 +0100 Subject: [PATCH 016/135] Add sanity check for 'build/lib/build/lib' when creating distribution fixtures --- setuptools/tests/fixtures.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/setuptools/tests/fixtures.py b/setuptools/tests/fixtures.py index 629daf93d4..1b8f520e84 100644 --- a/setuptools/tests/fixtures.py +++ b/setuptools/tests/fixtures.py @@ -77,6 +77,10 @@ def setuptools_sdist(tmp_path_factory, request): if dist: return dist + # Sanity check + # Building should not create recursive `setuptools/build/lib/build/lib/...` + assert not Path(request.config.rootdir, "build/lib/build").exists() + subprocess.check_output([ sys.executable, "-m", @@ -86,6 +90,11 @@ def setuptools_sdist(tmp_path_factory, request): str(tmp), str(request.config.rootdir), ]) + + # Sanity check + # Building should not create recursive `setuptools/build/lib/build/lib/...` + assert not Path(request.config.rootdir, "build/lib/build").exists() + return next(tmp.glob("*.tar.gz")) @@ -102,6 +111,10 @@ def setuptools_wheel(tmp_path_factory, request): if dist: return dist + # Sanity check + # Building should not create recursive `setuptools/build/lib/build/lib/...` + assert not Path(request.config.rootdir, "build/lib/build").exists() + subprocess.check_output([ sys.executable, "-m", @@ -111,6 +124,11 @@ def setuptools_wheel(tmp_path_factory, request): str(tmp), str(request.config.rootdir), ]) + + # Sanity check + # Building should not create recursive `setuptools/build/lib/build/lib/...` + assert not Path(request.config.rootdir, "build/lib/build").exists() + return next(tmp.glob("*.whl")) From f1ecea0486c8ea6fcf388b48ef0aae08e5fd8feb Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 16 Apr 2024 20:04:04 +0100 Subject: [PATCH 017/135] Avoid setuptools_wheel fixture to create recursively nested build/lib/build/lib/... directories Based on the test introduced in b4d3e83f0, we can see that when none of `PRE_BUILT_SETUPTOOLS_SDIST` or `PRE_BUILT_SETUPTOOLS_WHEEL` is set, the `setuptools_wheel` fixture keeps recursively creating `build/lib/build/lib/...` directories which slows down the tests and creates a huge amount of unnecessary files. This change tries to target that. --- setuptools/tests/fixtures.py | 63 +++++++++++++----------------------- 1 file changed, 23 insertions(+), 40 deletions(-) diff --git a/setuptools/tests/fixtures.py b/setuptools/tests/fixtures.py index 1b8f520e84..a2870f11e1 100644 --- a/setuptools/tests/fixtures.py +++ b/setuptools/tests/fixtures.py @@ -63,73 +63,56 @@ def sample_project(tmp_path): # 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" +# In the case of setuptools, building the wheel without sdist may cause +# it to contain the `build` directory, and therefore create situations with +# `setuptools/build/lib/build/lib/...`. To avoid that, build both artifacts at once. -@pytest.fixture(scope="session") -def setuptools_sdist(tmp_path_factory, request): - prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_SDIST") - if prebuilt and os.path.exists(prebuilt): # pragma: no cover - return Path(prebuilt).resolve() +def _build_distributions(tmp_path_factory, request): with contexts.session_locked_tmp_dir( - request, tmp_path_factory, "sdist_build" + request, tmp_path_factory, "dist_build" ) as tmp: # pragma: no cover - dist = next(tmp.glob("*.tar.gz"), None) - if dist: - return dist + sdist = next(tmp.glob("*.tar.gz"), None) + wheel = next(tmp.glob("*.whl"), None) + if sdist and wheel: + return (sdist, wheel) - # Sanity check - # Building should not create recursive `setuptools/build/lib/build/lib/...` + # Sanity check: should not create recursive setuptools/build/lib/build/lib/... assert not Path(request.config.rootdir, "build/lib/build").exists() subprocess.check_output([ sys.executable, "-m", "build", - "--sdist", "--outdir", str(tmp), str(request.config.rootdir), ]) - # Sanity check - # Building should not create recursive `setuptools/build/lib/build/lib/...` + # Sanity check: should not create recursive setuptools/build/lib/build/lib/... assert not Path(request.config.rootdir, "build/lib/build").exists() - return next(tmp.glob("*.tar.gz")) + return next(tmp.glob("*.tar.gz")), next(tmp.glob("*.whl")) @pytest.fixture(scope="session") -def setuptools_wheel(tmp_path_factory, request): - prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_WHEEL") +def setuptools_sdist(tmp_path_factory, request): + prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_SDIST") if prebuilt and os.path.exists(prebuilt): # pragma: no cover return Path(prebuilt).resolve() - with contexts.session_locked_tmp_dir( - request, tmp_path_factory, "wheel_build" - ) as tmp: # pragma: no cover - dist = next(tmp.glob("*.whl"), None) - if dist: - return dist + sdist, _ = _build_distributions(tmp_path_factory, request) + return sdist - # Sanity check - # Building should not create recursive `setuptools/build/lib/build/lib/...` - assert not Path(request.config.rootdir, "build/lib/build").exists() - subprocess.check_output([ - sys.executable, - "-m", - "build", - "--wheel", - "--outdir", - str(tmp), - str(request.config.rootdir), - ]) - - # Sanity check - # Building should not create recursive `setuptools/build/lib/build/lib/...` - assert not Path(request.config.rootdir, "build/lib/build").exists() +@pytest.fixture(scope="session") +def setuptools_wheel(tmp_path_factory, request): + prebuilt = os.getenv("PRE_BUILT_SETUPTOOLS_WHEEL") + if prebuilt and os.path.exists(prebuilt): # pragma: no cover + return Path(prebuilt).resolve() - return next(tmp.glob("*.whl")) + _, wheel = _build_distributions(tmp_path_factory, request) + return wheel @pytest.fixture From 14ce35000023749b41015c7b2884080be3803768 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 16 Apr 2024 20:17:06 +0100 Subject: [PATCH 018/135] Add news fragment --- newsfragments/4308.misc.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 newsfragments/4308.misc.rst diff --git a/newsfragments/4308.misc.rst b/newsfragments/4308.misc.rst new file mode 100644 index 0000000000..6c43f6338e --- /dev/null +++ b/newsfragments/4308.misc.rst @@ -0,0 +1,2 @@ +Fix ``setuptools_wheel`` fixture and avoid the recursive creation of +``build/lib/build/lib/build/...`` directories in the project root during tests. From 153d75eaa3f2009d0a2e5d47a729428c24fc8913 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 16 Apr 2024 19:25:12 +0100 Subject: [PATCH 019/135] Refactor _TopLevelFinder so it is easier to test --- setuptools/command/editable_wheel.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index 4d21e2253f..1167346069 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -505,7 +505,7 @@ def __init__(self, dist: Distribution, name: str): self.dist = dist self.name = name - def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str]): + def template_vars(self) -> Tuple[str, str, Dict[str, str], Dict[str, List[str]]]: src_root = self.dist.src_root or os.curdir top_level = chain(_find_packages(self.dist), _find_top_level_modules(self.dist)) package_dir = self.dist.package_dir or {} @@ -519,7 +519,7 @@ def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str] ) legacy_namespaces = { - pkg: find_package_path(pkg, roots, self.dist.src_root or "") + cast(str, pkg): find_package_path(pkg, roots, self.dist.src_root or "") for pkg in self.dist.namespace_packages or [] } @@ -530,11 +530,20 @@ def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str] name = f"__editable__.{self.name}.finder" finder = _normalization.safe_identifier(name) + return finder, name, mapping, namespaces_ + + def get_implementation(self) -> Iterator[Tuple[str, bytes]]: + finder, name, mapping, namespaces_ = self.template_vars() + content = bytes(_finder_template(name, mapping, namespaces_), "utf-8") - wheel.writestr(f"{finder}.py", content) + yield (f"{finder}.py", content) content = _encode_pth(f"import {finder}; {finder}.install()") - wheel.writestr(f"__editable__.{self.name}.pth", content) + yield (f"__editable__.{self.name}.pth", content) + + def __call__(self, wheel: "WheelFile", files: List[str], mapping: Dict[str, str]): + for file, content in self.get_implementation(): + wheel.writestr(file, content) def __enter__(self): msg = "Editable install will be performed using a meta path finder.\n" From a39d3623f2d9088a6914cfa833838ceac182af57 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 16 Apr 2024 19:25:48 +0100 Subject: [PATCH 020/135] Add test for issue 4248 --- setuptools/tests/test_editable_install.py | 44 +++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py index 1df09fd256..5da4fccefa 100644 --- a/setuptools/tests/test_editable_install.py +++ b/setuptools/tests/test_editable_install.py @@ -24,6 +24,7 @@ from setuptools.command.editable_wheel import ( _DebuggingTips, _LinkTree, + _TopLevelFinder, _encode_pth, _find_virtual_namespaces, _find_namespaces, @@ -530,6 +531,49 @@ def test_combine_namespaces(self, tmp_path): assert pkgA.a == 13 assert mod2.b == 37 + def test_combine_namespaces_nested(self, tmp_path): + """ + Users may attempt to combine namespace packages in a nested way via + ``package_dir`` as shown in pypa/setuptools#4248. + """ + + files = { + "src": {"my_package": {"my_module.py": "a = 13"}}, + "src2": {"my_package2": {"my_module2.py": "b = 37"}}, + } + + stack = jaraco.path.DirectoryStack() + with stack.context(tmp_path): + jaraco.path.build(files) + attrs = { + "script_name": "%PEP 517%", + "package_dir": { + "different_name": "src/my_package", + "different_name.subpkg": "src2/my_package2", + }, + "packages": ["different_name", "different_name.subpkg"], + } + dist = Distribution(attrs) + finder = _TopLevelFinder(dist, str(uuid4())) + code = next(v for k, v in finder.get_implementation() if k.endswith(".py")) + + with contexts.save_paths(), contexts.save_sys_modules(): + for mod in attrs["packages"]: + sys.modules.pop(mod, None) + + self.install_finder(code) + mod1 = import_module("different_name.my_module") + mod2 = import_module("different_name.subpkg.my_module2") + + expected = str((tmp_path / "src/my_package/my_module.py").resolve()) + assert str(Path(mod1.__file__).resolve()) == expected + + expected = str((tmp_path / "src2/my_package2/my_module2.py").resolve()) + assert str(Path(mod2.__file__).resolve()) == expected + + assert mod1.a == 13 + assert mod2.b == 37 + def test_dynamic_path_computation(self, tmp_path): # Follows the example in PEP 420 files = { From 19b63d1b81cd0544e2718f82c482efdc99742ef8 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Thu, 7 Mar 2024 02:46:09 +0000 Subject: [PATCH 021/135] Fix PathEntryFinder / MetaPathFinder in editable_wheel It seems that the import machinery skips PathEntryFinder when trying to locate nested namespaces, if the `sys.path_hook` item corresponding to the finder cannot be located in the submodule locations of the parent namespace. This means that we should probably always add the PATH_PLACEHOLDER to the namespace spec. This PR also add some type hints to the template, because it helped to debug some type errors. --- setuptools/command/editable_wheel.py | 32 +++++++++++++++++----------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index 1167346069..1722817f82 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -793,6 +793,7 @@ def _get_root(self): _FINDER_TEMPLATE = """\ +from __future__ import annotations import sys from importlib.machinery import ModuleSpec, PathFinder from importlib.machinery import all_suffixes as module_suffixes @@ -800,16 +801,14 @@ def _get_root(self): from itertools import chain from pathlib import Path -MAPPING = {mapping!r} -NAMESPACES = {namespaces!r} +MAPPING: dict[str, str] = {mapping!r} +NAMESPACES: dict[str, list[str]] = {namespaces!r} PATH_PLACEHOLDER = {name!r} + ".__path_hook__" class _EditableFinder: # MetaPathFinder @classmethod - def find_spec(cls, fullname, path=None, target=None): - extra_path = [] - + def find_spec(cls, fullname: str, _path=None, _target=None) -> ModuleSpec | None: # Top-level packages and modules (we know these exist in the FS) if fullname in MAPPING: pkg_path = MAPPING[fullname] @@ -820,35 +819,42 @@ def find_spec(cls, fullname, path=None, target=None): # to the importlib.machinery implementation. parent, _, child = fullname.rpartition(".") if parent and parent in MAPPING: - return PathFinder.find_spec(fullname, path=[MAPPING[parent], *extra_path]) + return PathFinder.find_spec(fullname, path=[MAPPING[parent]]) # Other levels of nesting should be handled automatically by importlib # using the parent path. return None @classmethod - def _find_spec(cls, fullname, candidate_path): + def _find_spec(cls, fullname: str, candidate_path: Path) -> ModuleSpec | None: init = candidate_path / "__init__.py" candidates = (candidate_path.with_suffix(x) for x in module_suffixes()) for candidate in chain([init], candidates): if candidate.exists(): return spec_from_file_location(fullname, candidate) + return None class _EditableNamespaceFinder: # PathEntryFinder @classmethod - def _path_hook(cls, path): + def _path_hook(cls, path) -> type[_EditableNamespaceFinder]: if path == PATH_PLACEHOLDER: return cls raise ImportError @classmethod - def _paths(cls, fullname): - # Ensure __path__ is not empty for the spec to be considered a namespace. - return NAMESPACES[fullname] or MAPPING.get(fullname) or [PATH_PLACEHOLDER] + def _paths(cls, fullname: str) -> list[str]: + paths = NAMESPACES[fullname] + if not paths and fullname in MAPPING: + paths = [MAPPING[fullname]] + # Always add placeholder, for 2 reasons: + # 1. __path__ cannot be empty for the spec to be considered namespace. + # 2. In the case of nested namespaces, we need to force + # import machinery to query _EditableNamespaceFinder again. + return [*paths, PATH_PLACEHOLDER] @classmethod - def find_spec(cls, fullname, target=None): + def find_spec(cls, fullname: str, _target=None) -> ModuleSpec | None: if fullname in NAMESPACES: spec = ModuleSpec(fullname, None, is_package=True) spec.submodule_search_locations = cls._paths(fullname) @@ -856,7 +862,7 @@ def find_spec(cls, fullname, target=None): return None @classmethod - def find_module(cls, fullname): + def find_module(cls, _fullname) -> None: return None From 2b7ea603325bf8f2edbc7f12cc6b8cb2a7bd41e7 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 16 Apr 2024 19:35:57 +0100 Subject: [PATCH 022/135] Add news fragment --- newsfragments/4278.bugfix.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 newsfragments/4278.bugfix.rst diff --git a/newsfragments/4278.bugfix.rst b/newsfragments/4278.bugfix.rst new file mode 100644 index 0000000000..5e606cced8 --- /dev/null +++ b/newsfragments/4278.bugfix.rst @@ -0,0 +1,2 @@ +Fix finder template for lenient editable installs of implicit nested namespaces +constructed by using ``package_dir`` to reorganise directory structure. From 3d539f30913fedb6bf1e3bb9f4ef52de09a9eb09 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 10:16:27 +0100 Subject: [PATCH 023/135] Convert safe txt files to UTF-8 --- setuptools/command/bdist_egg.py | 5 ++--- setuptools/dist.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 3687efdf9c..b2897bfbb4 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -350,9 +350,8 @@ def write_safety_flag(egg_dir, safe): if safe is None or bool(safe) != flag: os.unlink(fn) elif safe is not None and bool(safe) == flag: - f = open(fn, 'wt') - f.write('\n') - f.close() + with open(fn, 'wt', encoding="utf-8") as f: + f.write('\n') safety_flags = { diff --git a/setuptools/dist.py b/setuptools/dist.py index 6350e38100..076f9a2327 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -685,7 +685,7 @@ def get_egg_cache_dir(self): os.mkdir(egg_cache_dir) windows_support.hide_file(egg_cache_dir) readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') - with open(readme_txt_filename, 'w') as f: + with open(readme_txt_filename, 'w', encoding="utf-8") as f: f.write( 'This directory contains eggs that were downloaded ' 'by setuptools to build, test, and run plug-ins.\n\n' From d6651219d5284b2703b9849fe5fe2fcb47a54230 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 10:19:05 +0100 Subject: [PATCH 024/135] Try to read some files as UTF-8 before attempting locale --- setuptools/command/setopt.py | 11 +++++++++-- setuptools/package_index.py | 16 +++++++++++++--- setuptools/wheel.py | 11 +++++++++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index f9a6075128..aa800492f7 100644 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -5,7 +5,8 @@ import os import configparser -from setuptools import Command +from .. import Command +from ..compat import py39 __all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] @@ -36,7 +37,13 @@ def edit_config(filename, settings, dry_run=False): log.debug("Reading configuration from %s", filename) opts = configparser.RawConfigParser() opts.optionxform = lambda x: x - opts.read([filename]) + + try: + opts.read([filename], encoding="utf-8") + except UnicodeDecodeError: # pragma: no cover + opts.clear() + opts.read([filename], encoding=py39.LOCALE_ENCODING) + for section, options in settings.items(): if options is None: log.info("Deleting section [%s] from %s", section, filename) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 271aa97f71..f835bdcf14 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -40,6 +40,8 @@ from setuptools.wheel import Wheel from setuptools.extern.more_itertools import unique_everseen +from .compat import py39 + EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') HREF = re.compile(r"""href\s*=\s*['"]?([^'"> ]+)""", re.I) @@ -1011,7 +1013,11 @@ def __init__(self): rc = os.path.join(os.path.expanduser('~'), '.pypirc') if os.path.exists(rc): - self.read(rc) + try: + self.read(rc, encoding="utf-8") + except UnicodeDecodeError: # pragma: no cover + self.clean() + self.read(rc, encoding=py39.LOCALE_ENCODING) @property def creds_by_repository(self): @@ -1114,8 +1120,12 @@ def local_open(url): for f in os.listdir(filename): filepath = os.path.join(filename, f) if f == 'index.html': - with open(filepath, 'r') as fp: - body = fp.read() + try: + with open(filepath, 'r', encoding="utf-8") as fp: + body = fp.read() + except UnicodeDecodeError: # pragma: no cover + with open(filepath, 'r', encoding=py39.LOCALE_ENCODING) as fp: + body = fp.read() break elif os.path.isdir(filepath): f += '/' diff --git a/setuptools/wheel.py b/setuptools/wheel.py index 9861b5cf1c..4cf3f4ca0d 100644 --- a/setuptools/wheel.py +++ b/setuptools/wheel.py @@ -18,6 +18,8 @@ from setuptools.command.egg_info import write_requirements, _egg_basename from setuptools.archive_util import _unpack_zipfile_obj +from .compat import py39 + WHEEL_NAME = re.compile( r"""^(?P.+?)-(?P\d.*?) @@ -222,8 +224,13 @@ def _move_data_entries(destination_eggdir, dist_data): def _fix_namespace_packages(egg_info, destination_eggdir): namespace_packages = os.path.join(egg_info, 'namespace_packages.txt') if os.path.exists(namespace_packages): - with open(namespace_packages) as fp: - namespace_packages = fp.read().split() + try: + with open(namespace_packages, encoding="utf-8") as fp: + namespace_packages = fp.read().split() + except UnicodeDecodeError: # pragma: no cover + with open(namespace_packages, encoding=py39.LOCALE_ENCODING) as fp: + namespace_packages = fp.read().split() + for mod in namespace_packages: mod_dir = os.path.join(destination_eggdir, *mod.split('.')) mod_init = os.path.join(mod_dir, '__init__.py') From e8e59831978216b60712c1411d9f6bd6eabebe23 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 10:19:59 +0100 Subject: [PATCH 025/135] Attempt to use utf-8 with Popen --- setuptools/tests/environment.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py index df2bd37ff6..6173936f7b 100644 --- a/setuptools/tests/environment.py +++ b/setuptools/tests/environment.py @@ -76,6 +76,7 @@ def run_setup_py(cmd, pypath=None, path=None, data_stream=0, env=None): stderr=_PIPE, shell=shell, env=env, + encoding="utf-8", ) if isinstance(data_stream, tuple): From 13fdfaa2c9aa8a9decd8ec6624e536e2ed9cc7c6 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 10:21:18 +0100 Subject: [PATCH 026/135] Use utf-8 for NAMESPACE_PACKAGE_INIT This change should be relatively safe --- setuptools/wheel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/wheel.py b/setuptools/wheel.py index 4cf3f4ca0d..19f4157423 100644 --- a/setuptools/wheel.py +++ b/setuptools/wheel.py @@ -237,5 +237,5 @@ def _fix_namespace_packages(egg_info, destination_eggdir): if not os.path.exists(mod_dir): os.mkdir(mod_dir) if not os.path.exists(mod_init): - with open(mod_init, 'w') as fp: + with open(mod_init, 'w', encoding="utf-8") as fp: fp.write(NAMESPACE_PACKAGE_INIT) From 851e972aae6efa2cf6ead909db7653bce4368c5f Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 10:22:08 +0100 Subject: [PATCH 027/135] Attempt to use UTF-8 when rewriting 'setup.cfg' --- setuptools/command/setopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index aa800492f7..89b1ac7307 100644 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -69,7 +69,7 @@ def edit_config(filename, settings, dry_run=False): log.info("Writing %s", filename) if not dry_run: - with open(filename, 'w') as f: + with open(filename, 'w', encoding="utf-8") as f: opts.write(f) From 20c0f82778a82d57c061f57d56c99192294e0acf Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 10:24:42 +0100 Subject: [PATCH 028/135] Attempt to use UTF-8 when writing str scripts with easy_install/install_scripts --- setuptools/command/easy_install.py | 8 +++++++- setuptools/command/install_scripts.py | 10 +++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 87a68c292a..3ad984f212 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -873,7 +873,13 @@ def write_script(self, script_name, contents, mode="t", blockers=()): ensure_directory(target) if os.path.exists(target): os.unlink(target) - with open(target, "w" + mode) as f: # TODO: is it safe to use utf-8? + + if "b" not in mode and isinstance(contents, str): + kw = {"encoding": "utf-8"} + else: + kw = {} + + with open(target, "w" + mode, **kw) as f: f.write(contents) chmod(target, 0o777 - mask) diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py index 72b2e45cbc..758937b614 100644 --- a/setuptools/command/install_scripts.py +++ b/setuptools/command/install_scripts.py @@ -57,10 +57,14 @@ def write_script(self, script_name, contents, mode="t", *ignored): target = os.path.join(self.install_dir, script_name) self.outfiles.append(target) + if "b" not in mode and isinstance(contents, str): + kw = {"encoding": "utf-8"} + else: + kw = {} + mask = current_umask() if not self.dry_run: ensure_directory(target) - f = open(target, "w" + mode) - f.write(contents) - f.close() + with open(target, "w" + mode, **kw) as f: + f.write(contents) chmod(target, 0o777 - mask) From e44a63cb16a19d31d4d0c080efb71aac63ff0948 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 10:28:43 +0100 Subject: [PATCH 029/135] Prevent missing UTF-8 warnings in setuptools._imp This should be safe because we use `tokenize.open` for source codes. --- setuptools/_imp.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setuptools/_imp.py b/setuptools/_imp.py index 9d4ead0eb0..38b146fc4d 100644 --- a/setuptools/_imp.py +++ b/setuptools/_imp.py @@ -6,6 +6,7 @@ import os import importlib.util import importlib.machinery +import tokenize from importlib.util import module_from_spec @@ -60,13 +61,13 @@ def find_module(module, paths=None): if suffix in importlib.machinery.SOURCE_SUFFIXES: kind = PY_SOURCE + file = tokenize.open(path) elif suffix in importlib.machinery.BYTECODE_SUFFIXES: kind = PY_COMPILED + file = open(path, 'rb') elif suffix in importlib.machinery.EXTENSION_SUFFIXES: kind = C_EXTENSION - if kind in {PY_SOURCE, PY_COMPILED}: - file = open(path, mode) else: path = None suffix = mode = '' From b540f93cdf7edd0a0ce3e06a23ff4714bf13420e Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 16:01:56 +0100 Subject: [PATCH 030/135] Use UTF-8 with venv.run and avoid encoding warnings --- setuptools/tests/environment.py | 2 +- setuptools/tests/test_build_meta.py | 4 +- setuptools/tests/test_editable_install.py | 66 +++++++++++------------ 3 files changed, 36 insertions(+), 36 deletions(-) diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py index 6173936f7b..b9de4fda6b 100644 --- a/setuptools/tests/environment.py +++ b/setuptools/tests/environment.py @@ -17,7 +17,7 @@ class VirtualEnv(jaraco.envs.VirtualEnv): def run(self, cmd, *args, **kwargs): cmd = [self.exe(cmd[0])] + cmd[1:] - kwargs = {"cwd": self.root, **kwargs} # Allow overriding + kwargs = {"cwd": self.root, "encoding": "utf-8", **kwargs} # Allow overriding # In some environments (eg. downstream distro packaging), where: # - tox isn't used to run tests and # - PYTHONPATH is set to point to a specific setuptools codebase and diff --git a/setuptools/tests/test_build_meta.py b/setuptools/tests/test_build_meta.py index c2a1e6dc75..43830feb77 100644 --- a/setuptools/tests/test_build_meta.py +++ b/setuptools/tests/test_build_meta.py @@ -941,14 +941,14 @@ def test_legacy_editable_install(venv, tmpdir, tmpdir_cwd): # First: sanity check cmd = ["pip", "install", "--no-build-isolation", "-e", "."] - output = str(venv.run(cmd, cwd=tmpdir), "utf-8").lower() + output = venv.run(cmd, cwd=tmpdir).lower() assert "running setup.py develop for myproj" not in output assert "created wheel for myproj" in output # Then: real test env = {**os.environ, "SETUPTOOLS_ENABLE_FEATURES": "legacy-editable"} cmd = ["pip", "install", "--no-build-isolation", "-e", "."] - output = str(venv.run(cmd, cwd=tmpdir, env=env), "utf-8").lower() + output = venv.run(cmd, cwd=tmpdir, env=env).lower() assert "running setup.py develop for myproj" in output diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py index 5da4fccefa..119b128694 100644 --- a/setuptools/tests/test_editable_install.py +++ b/setuptools/tests/test_editable_install.py @@ -131,7 +131,7 @@ def test_editable_with_pyproject(tmp_path, venv, files, editable_opts): jaraco.path.build(files, prefix=project) cmd = [ - venv.exe(), + "python", "-m", "pip", "install", @@ -140,14 +140,14 @@ def test_editable_with_pyproject(tmp_path, venv, files, editable_opts): str(project), *editable_opts, ] - print(str(subprocess.check_output(cmd), "utf-8")) + print(venv.run(cmd)) - cmd = [venv.exe(), "-m", "mypkg"] - assert subprocess.check_output(cmd).strip() == b"3.14159.post0 Hello World" + cmd = ["python", "-m", "mypkg"] + assert venv.run(cmd).strip() == "3.14159.post0 Hello World" (project / "src/mypkg/data.txt").write_text("foobar", encoding="utf-8") (project / "src/mypkg/mod.py").write_text("x = 42", encoding="utf-8") - assert subprocess.check_output(cmd).strip() == b"3.14159.post0 foobar 42" + assert venv.run(cmd).strip() == "3.14159.post0 foobar 42" def test_editable_with_flat_layout(tmp_path, venv, editable_opts): @@ -176,7 +176,7 @@ def test_editable_with_flat_layout(tmp_path, venv, editable_opts): project = tmp_path / "mypkg" cmd = [ - venv.exe(), + "python", "-m", "pip", "install", @@ -185,9 +185,9 @@ def test_editable_with_flat_layout(tmp_path, venv, editable_opts): str(project), *editable_opts, ] - print(str(subprocess.check_output(cmd), "utf-8")) - cmd = [venv.exe(), "-c", "import pkg, mod; print(pkg.a, mod.b)"] - assert subprocess.check_output(cmd).strip() == b"4 2" + print(venv.run(cmd)) + cmd = ["python", "-c", "import pkg, mod; print(pkg.a, mod.b)"] + assert venv.run(cmd).strip() == "4 2" def test_editable_with_single_module(tmp_path, venv, editable_opts): @@ -214,7 +214,7 @@ def test_editable_with_single_module(tmp_path, venv, editable_opts): project = tmp_path / "mypkg" cmd = [ - venv.exe(), + "python", "-m", "pip", "install", @@ -223,9 +223,9 @@ def test_editable_with_single_module(tmp_path, venv, editable_opts): str(project), *editable_opts, ] - print(str(subprocess.check_output(cmd), "utf-8")) - cmd = [venv.exe(), "-c", "import mod; print(mod.b)"] - assert subprocess.check_output(cmd).strip() == b"2" + print(venv.run(cmd)) + cmd = ["python", "-c", "import mod; print(mod.b)"] + assert venv.run(cmd).strip() == "2" class TestLegacyNamespaces: @@ -384,7 +384,7 @@ def test_namespace_accidental_config_in_lenient_mode(self, venv, tmp_path): opts = ["--no-build-isolation"] # force current version of setuptools venv.run(["python", "-m", "pip", "-v", "install", "-e", str(pkg_A), *opts]) out = venv.run(["python", "-c", "from mypkg.n import pkgA; print(pkgA.a)"]) - assert str(out, "utf-8").strip() == "1" + assert out.strip() == "1" cmd = """\ try: import mypkg.other @@ -392,7 +392,7 @@ def test_namespace_accidental_config_in_lenient_mode(self, venv, tmp_path): print("mypkg.other not defined") """ out = venv.run(["python", "-c", dedent(cmd)]) - assert "mypkg.other not defined" in str(out, "utf-8") + assert "mypkg.other not defined" in out # Moved here from test_develop: @@ -911,7 +911,7 @@ def test_editable_install(self, tmp_path, venv, layout, editable_opts): print(ex) """ out = venv.run(["python", "-c", dedent(cmd_import_error)]) - assert b"No module named 'otherfile'" in out + assert "No module named 'otherfile'" in out # Ensure the modules are importable cmd_get_vars = """\ @@ -919,7 +919,7 @@ def test_editable_install(self, tmp_path, venv, layout, editable_opts): print(mypkg.mod1.var, mypkg.subpackage.mod2.var) """ out = venv.run(["python", "-c", dedent(cmd_get_vars)]) - assert b"42 13" in out + assert "42 13" in out # Ensure resources are reachable cmd_get_resource = """\ @@ -929,7 +929,7 @@ def test_editable_install(self, tmp_path, venv, layout, editable_opts): print(text.read_text(encoding="utf-8")) """ out = venv.run(["python", "-c", dedent(cmd_get_resource)]) - assert b"resource 39" in out + assert "resource 39" in out # Ensure files are editable mod1 = next(project.glob("**/mod1.py")) @@ -941,12 +941,12 @@ def test_editable_install(self, tmp_path, venv, layout, editable_opts): resource_file.write_text("resource 374", encoding="utf-8") out = venv.run(["python", "-c", dedent(cmd_get_vars)]) - assert b"42 13" not in out - assert b"17 781" in out + assert "42 13" not in out + assert "17 781" in out out = venv.run(["python", "-c", dedent(cmd_get_resource)]) - assert b"resource 39" not in out - assert b"resource 374" in out + assert "resource 39" not in out + assert "resource 374" in out class TestLinkTree: @@ -1005,7 +1005,7 @@ def test_strict_install(self, tmp_path, venv): install_project("mypkg", venv, tmp_path, self.FILES, *opts) out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) - assert b"42" in out + assert "42" in out # Ensure packages excluded from distribution are not importable cmd_import_error = """\ @@ -1015,7 +1015,7 @@ def test_strict_install(self, tmp_path, venv): print(ex) """ out = venv.run(["python", "-c", dedent(cmd_import_error)]) - assert b"cannot import name 'subpackage'" in out + assert "cannot import name 'subpackage'" in out # Ensure resource files excluded from distribution are not reachable cmd_get_resource = """\ @@ -1028,8 +1028,8 @@ def test_strict_install(self, tmp_path, venv): print(ex) """ out = venv.run(["python", "-c", dedent(cmd_get_resource)]) - assert b"No such file or directory" in out - assert b"resource.not_in_manifest" in out + assert "No such file or directory" in out + assert "resource.not_in_manifest" in out @pytest.mark.filterwarnings("ignore:.*compat.*:setuptools.SetuptoolsDeprecationWarning") @@ -1040,7 +1040,7 @@ def test_compat_install(tmp_path, venv): install_project("mypkg", venv, tmp_path, files, *opts) out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) - assert b"42" in out + assert "42" in out expected_path = comparable_path(str(tmp_path)) @@ -1051,7 +1051,7 @@ def test_compat_install(tmp_path, venv): "import other; print(other)", "import mypkg; print(mypkg)", ): - out = comparable_path(str(venv.run(["python", "-c", cmd]), "utf-8")) + out = comparable_path(venv.run(["python", "-c", cmd])) assert expected_path in out # Compatible behaviour will not consider custom mappings @@ -1061,7 +1061,7 @@ def test_compat_install(tmp_path, venv): except ImportError as ex: print(ex) """ - out = str(venv.run(["python", "-c", dedent(cmd)]), "utf-8") + out = venv.run(["python", "-c", dedent(cmd)]) assert "cannot import name 'subpackage'" in out @@ -1105,7 +1105,7 @@ def test_pbr_integration(tmp_path, venv, editable_opts): install_project("mypkg", venv, tmp_path, files, *editable_opts) out = venv.run(["python", "-c", "import mypkg.hello"]) - assert b"Hello world!" in out + assert "Hello world!" in out class TestCustomBuildPy: @@ -1143,11 +1143,11 @@ def test_safeguarded_from_errors(self, tmp_path, venv): """Ensure that errors in custom build_py are reported as warnings""" # Warnings should show up _, out = install_project("mypkg", venv, tmp_path, self.FILES) - assert b"SetuptoolsDeprecationWarning" in out - assert b"ValueError: TEST_RAISE" in out + assert "SetuptoolsDeprecationWarning" in out + assert "ValueError: TEST_RAISE" in out # but installation should be successful out = venv.run(["python", "-c", "import mypkg.mod1; print(mypkg.mod1.var)"]) - assert b"42" in out + assert "42" in out class TestCustomBuildWheel: From eae4e26b0a1fb9ff8ca31ca2be4b58220b59a32f Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 17:56:36 +0100 Subject: [PATCH 031/135] Ignore warning caused by 3rd-party setup.py --- setuptools/tests/test_integration.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py index 1aa16172b5..77ef733698 100644 --- a/setuptools/tests/test_integration.py +++ b/setuptools/tests/test_integration.py @@ -99,6 +99,10 @@ def test_pbr(install_context): @pytest.mark.xfail +@pytest.mark.filterwarnings("ignore::EncodingWarning") +# ^-- Dependency chain: `python-novaclient` < `oslo-utils` < `netifaces==0.11.0` +# netifaces' setup.py uses `open` without `encoding="utf-8"` which is hijacked by +# `setuptools.sandbox._open` and triggers the EncodingWarning. def test_python_novaclient(install_context): _install_one('python-novaclient', install_context, 'novaclient', 'base.py') From 8a75f99ca60a8a78f1dbba94b2969d292ea5557c Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 17:57:26 +0100 Subject: [PATCH 032/135] Use UTF-8 in setuptools/tests/test_easy_install.py --- setuptools/tests/test_easy_install.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 950cb23d21..ada4c32285 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -361,7 +361,7 @@ def test_many_pth_distributions_merge_together(self, tmpdir): @pytest.fixture def setup_context(tmpdir): - with (tmpdir / 'setup.py').open('w') as f: + with (tmpdir / 'setup.py').open('w', encoding="utf-8") as f: f.write(SETUP_PY) with tmpdir.as_cwd(): yield tmpdir From 2b82912b4b4a4576cd6edfd307f14ff615f21ed4 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 17:57:49 +0100 Subject: [PATCH 033/135] Attempt to use UTF-8 with develop command. This change tries to use UTF-8 when writing `.egg-link` files. When reading other files, we first attempt to use UTF-8 and then fallback for the locale encoding. --- setuptools/command/develop.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index d8c1b49b3d..aeb491fe2c 100644 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -10,6 +10,8 @@ from setuptools import namespaces import setuptools +from ..compat import py39 + class develop(namespaces.DevelopInstaller, easy_install): """Set up package for development""" @@ -119,7 +121,7 @@ def install_for_development(self): # create an .egg-link in the installation dir, pointing to our egg log.info("Creating %s (link to %s)", self.egg_link, self.egg_base) if not self.dry_run: - with open(self.egg_link, "w") as f: + with open(self.egg_link, "w", encoding="utf-8") as f: f.write(self.egg_path + "\n" + self.setup_path) # postprocess the installed distro, fixing up .pth, installing scripts, # and handling requirements @@ -128,9 +130,16 @@ def install_for_development(self): def uninstall_link(self): if os.path.exists(self.egg_link): log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) - egg_link_file = open(self.egg_link) - contents = [line.rstrip() for line in egg_link_file] - egg_link_file.close() + + try: + with open(self.egg_link, encoding="utf-8") as egg_link_file: + contents = [line.rstrip() for line in egg_link_file] + except UnicodeDecodeError: # pragma: no cover + with open( + self.egg_link, encoding=py39.LOCALE_ENCODING + ) as egg_link_file: + contents = [line.rstrip() for line in egg_link_file] + if contents not in ([self.egg_path], [self.egg_path, self.setup_path]): log.warn("Link points to %s: uninstall aborted", contents) return @@ -156,8 +165,14 @@ def install_egg_scripts(self, dist): for script_name in self.distribution.scripts or []: script_path = os.path.abspath(convert_path(script_name)) script_name = os.path.basename(script_path) - with open(script_path) as strm: - script_text = strm.read() + + try: + with open(script_path, encoding="utf-8") as strm: + script_text = strm.read() + except UnicodeDecodeError: # pragma: no cover + with open(script_path, encoding=py39.LOCALE_ENCODING) as strm: + script_text = strm.read() + self.install_script(dist, script_name, script_text, script_path) return None From 74a622833716b9fbbf2b1a94c58b59d5defe0dd3 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 18:32:08 +0100 Subject: [PATCH 034/135] Refactor some try..excepts into read_utf8_with_fallback Extract common pattern for reading a file with UTF-8 into the unicode_utils module. --- setuptools/command/develop.py | 23 ++++++----------------- setuptools/package_index.py | 8 ++------ setuptools/unicode_utils.py | 17 +++++++++++++++++ setuptools/wheel.py | 9 ++------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index aeb491fe2c..9966681bad 100644 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -10,7 +10,7 @@ from setuptools import namespaces import setuptools -from ..compat import py39 +from ..unicode_utils import read_utf8_with_fallback class develop(namespaces.DevelopInstaller, easy_install): @@ -131,14 +131,10 @@ def uninstall_link(self): if os.path.exists(self.egg_link): log.info("Removing %s (link to %s)", self.egg_link, self.egg_base) - try: - with open(self.egg_link, encoding="utf-8") as egg_link_file: - contents = [line.rstrip() for line in egg_link_file] - except UnicodeDecodeError: # pragma: no cover - with open( - self.egg_link, encoding=py39.LOCALE_ENCODING - ) as egg_link_file: - contents = [line.rstrip() for line in egg_link_file] + contents = [ + line.rstrip() + for line in read_utf8_with_fallback(self.egg_link).splitlines() + ] if contents not in ([self.egg_path], [self.egg_path, self.setup_path]): log.warn("Link points to %s: uninstall aborted", contents) @@ -165,14 +161,7 @@ def install_egg_scripts(self, dist): for script_name in self.distribution.scripts or []: script_path = os.path.abspath(convert_path(script_name)) script_name = os.path.basename(script_path) - - try: - with open(script_path, encoding="utf-8") as strm: - script_text = strm.read() - except UnicodeDecodeError: # pragma: no cover - with open(script_path, encoding=py39.LOCALE_ENCODING) as strm: - script_text = strm.read() - + script_text = read_utf8_with_fallback(script_path) self.install_script(dist, script_name, script_text, script_path) return None diff --git a/setuptools/package_index.py b/setuptools/package_index.py index f835bdcf14..2aa8464162 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -41,6 +41,7 @@ from setuptools.extern.more_itertools import unique_everseen from .compat import py39 +from .unicode_utils import read_utf8_with_fallback EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') @@ -1120,12 +1121,7 @@ def local_open(url): for f in os.listdir(filename): filepath = os.path.join(filename, f) if f == 'index.html': - try: - with open(filepath, 'r', encoding="utf-8") as fp: - body = fp.read() - except UnicodeDecodeError: # pragma: no cover - with open(filepath, 'r', encoding=py39.LOCALE_ENCODING) as fp: - body = fp.read() + body = read_utf8_with_fallback(filepath) break elif os.path.isdir(filepath): f += '/' diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index d43dcc11f9..4bc67feba0 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -1,6 +1,8 @@ import unicodedata import sys +from .compat import py39 + # HFS Plus uses decomposed UTF-8 def decompose(path): @@ -42,3 +44,18 @@ def try_encode(string, enc): return string.encode(enc) except UnicodeEncodeError: return None + + +def read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) -> str: + """ + First try to read the file with UTF-8, if there is an error fallback to a + different encoding ("locale" by default). Returns the content of the file. + Also useful when reading files that might have been produced by an older version of + setuptools. + """ + try: + with open(file, "r", encoding="utf-8") as f: + return f.read() + except UnicodeDecodeError: # pragma: no cover + with open(file, "r", encoding=fallback_encoding) as f: + return f.read() diff --git a/setuptools/wheel.py b/setuptools/wheel.py index 19f4157423..babd45940f 100644 --- a/setuptools/wheel.py +++ b/setuptools/wheel.py @@ -18,7 +18,7 @@ from setuptools.command.egg_info import write_requirements, _egg_basename from setuptools.archive_util import _unpack_zipfile_obj -from .compat import py39 +from .unicode_utils import read_utf8_with_fallback WHEEL_NAME = re.compile( @@ -224,12 +224,7 @@ def _move_data_entries(destination_eggdir, dist_data): def _fix_namespace_packages(egg_info, destination_eggdir): namespace_packages = os.path.join(egg_info, 'namespace_packages.txt') if os.path.exists(namespace_packages): - try: - with open(namespace_packages, encoding="utf-8") as fp: - namespace_packages = fp.read().split() - except UnicodeDecodeError: # pragma: no cover - with open(namespace_packages, encoding=py39.LOCALE_ENCODING) as fp: - namespace_packages = fp.read().split() + namespace_packages = read_utf8_with_fallback(namespace_packages).split() for mod in namespace_packages: mod_dir = os.path.join(destination_eggdir, *mod.split('.')) From aeac45b14d910044e2c1f0d2faec231b8c41fbeb Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 17 Apr 2024 19:05:14 +0100 Subject: [PATCH 035/135] Avoid using EncodingWarning because it is not defined for Python < 3.10 --- setuptools/tests/test_integration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/tests/test_integration.py b/setuptools/tests/test_integration.py index 77ef733698..71f10d9a6e 100644 --- a/setuptools/tests/test_integration.py +++ b/setuptools/tests/test_integration.py @@ -99,10 +99,11 @@ def test_pbr(install_context): @pytest.mark.xfail -@pytest.mark.filterwarnings("ignore::EncodingWarning") +@pytest.mark.filterwarnings("ignore:'encoding' argument not specified") # ^-- Dependency chain: `python-novaclient` < `oslo-utils` < `netifaces==0.11.0` # netifaces' setup.py uses `open` without `encoding="utf-8"` which is hijacked by # `setuptools.sandbox._open` and triggers the EncodingWarning. +# Can't use EncodingWarning in the filter, as it does not exist on Python < 3.10. def test_python_novaclient(install_context): _install_one('python-novaclient', install_context, 'novaclient', 'base.py') From 9fd598172bc4af6e90f95ff9916faf3e8717e497 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 10:59:52 +0100 Subject: [PATCH 036/135] Mark read_utf8_with_fallback as private --- setuptools/command/develop.py | 6 +++--- setuptools/package_index.py | 4 ++-- setuptools/unicode_utils.py | 2 +- setuptools/wheel.py | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py index 9966681bad..d07736a005 100644 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -10,7 +10,7 @@ from setuptools import namespaces import setuptools -from ..unicode_utils import read_utf8_with_fallback +from ..unicode_utils import _read_utf8_with_fallback class develop(namespaces.DevelopInstaller, easy_install): @@ -133,7 +133,7 @@ def uninstall_link(self): contents = [ line.rstrip() - for line in read_utf8_with_fallback(self.egg_link).splitlines() + for line in _read_utf8_with_fallback(self.egg_link).splitlines() ] if contents not in ([self.egg_path], [self.egg_path, self.setup_path]): @@ -161,7 +161,7 @@ def install_egg_scripts(self, dist): for script_name in self.distribution.scripts or []: script_path = os.path.abspath(convert_path(script_name)) script_name = os.path.basename(script_path) - script_text = read_utf8_with_fallback(script_path) + script_text = _read_utf8_with_fallback(script_path) self.install_script(dist, script_name, script_text, script_path) return None diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 2aa8464162..0ca87df357 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -41,7 +41,7 @@ from setuptools.extern.more_itertools import unique_everseen from .compat import py39 -from .unicode_utils import read_utf8_with_fallback +from .unicode_utils import _read_utf8_with_fallback EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') @@ -1121,7 +1121,7 @@ def local_open(url): for f in os.listdir(filename): filepath = os.path.join(filename, f) if f == 'index.html': - body = read_utf8_with_fallback(filepath) + body = _read_utf8_with_fallback(filepath) break elif os.path.isdir(filepath): f += '/' diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index 4bc67feba0..6b60417a91 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -46,7 +46,7 @@ def try_encode(string, enc): return None -def read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) -> str: +def _read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) -> str: """ First try to read the file with UTF-8, if there is an error fallback to a different encoding ("locale" by default). Returns the content of the file. diff --git a/setuptools/wheel.py b/setuptools/wheel.py index babd45940f..e06daec4d0 100644 --- a/setuptools/wheel.py +++ b/setuptools/wheel.py @@ -18,7 +18,7 @@ from setuptools.command.egg_info import write_requirements, _egg_basename from setuptools.archive_util import _unpack_zipfile_obj -from .unicode_utils import read_utf8_with_fallback +from .unicode_utils import _read_utf8_with_fallback WHEEL_NAME = re.compile( @@ -224,7 +224,7 @@ def _move_data_entries(destination_eggdir, dist_data): def _fix_namespace_packages(egg_info, destination_eggdir): namespace_packages = os.path.join(egg_info, 'namespace_packages.txt') if os.path.exists(namespace_packages): - namespace_packages = read_utf8_with_fallback(namespace_packages).split() + namespace_packages = _read_utf8_with_fallback(namespace_packages).split() for mod in namespace_packages: mod_dir = os.path.join(destination_eggdir, *mod.split('.')) From 9aa9f22e04b473af110461fea591560678bc1284 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 11:18:45 +0100 Subject: [PATCH 037/135] Add newsfragment --- newsfragments/4309.removal.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 newsfragments/4309.removal.rst diff --git a/newsfragments/4309.removal.rst b/newsfragments/4309.removal.rst new file mode 100644 index 0000000000..08818104f9 --- /dev/null +++ b/newsfragments/4309.removal.rst @@ -0,0 +1,5 @@ +Further adoption of UTF-8 in ``setuptools``. +This change regards mostly files produced and consumed during the build process +(e.g. metadata files, script wrappers, automatically updated config files, etc..) +Although precautions were taken to minimize disruptions, some edge cases might +be subject to backwards incompatibility. From 8b2009176279f977f6b6b50e8b8c4e4ba5b9f99e Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 11:25:18 +0100 Subject: [PATCH 038/135] Read files using UTF-8 in pkg_resources, with fallback to locale --- pkg_resources/__init__.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index c2ba0476e5..5d773da541 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -1524,8 +1524,7 @@ def run_script(self, script_name, namespace): script_filename = self._fn(self.egg_info, script) namespace['__file__'] = script_filename if os.path.exists(script_filename): - with open(script_filename) as fid: - source = fid.read() + source = _read_utf8_with_fallback(script_filename) code = compile(source, script_filename, 'exec') exec(code, namespace, namespace) else: @@ -2175,11 +2174,10 @@ def non_empty_lines(path): """ Yield non-empty lines from file at path """ - with open(path) as f: - for line in f: - line = line.strip() - if line: - yield line + for line in _read_utf8_with_fallback(path).splitlines(): + line = line.strip() + if line: + yield line def resolve_egg_link(path): @@ -3323,3 +3321,16 @@ def _initialize_master_working_set(): # match order list(map(working_set.add_entry, sys.path)) globals().update(locals()) + + +# ---- Ported from ``setuptools`` to avoid introducing dependencies ---- +LOCALE_ENCODING = "locale" if sys.version_info >= (3, 10) else None + + +def _read_utf8_with_fallback(file: str, fallback_encoding=LOCALE_ENCODING) -> str: + try: + with open(file, "r", encoding="utf-8") as f: + return f.read() + except UnicodeDecodeError: # pragma: no cover + with open(file, "r", encoding=fallback_encoding) as f: + return f.read() From 69f580687de2af6760fe171c81d84e6bfcc665ea Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 11:39:50 +0100 Subject: [PATCH 039/135] Use UTF-8 for writing python stubs Since Python3 is "UTF-8 first", this change should not cause problems. --- setuptools/command/bdist_egg.py | 2 +- setuptools/command/build_ext.py | 6 +++--- setuptools/package_index.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index b2897bfbb4..5581b1d2e0 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -54,7 +54,7 @@ def __bootstrap__(): __bootstrap__() """ ).lstrip() - with open(pyfile, 'w') as f: + with open(pyfile, 'w', encoding="utf-8") as f: f.write(_stub_template % resource) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index b5c98c86dc..49699d30ec 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -342,8 +342,7 @@ def _write_stub_file(self, stub_file: str, ext: Extension, compile=False): if compile and os.path.exists(stub_file): raise BaseError(stub_file + " already exists! Please delete.") if not self.dry_run: - f = open(stub_file, 'w') - f.write( + content = ( '\n'.join([ "def __bootstrap__():", " global __bootstrap__, __file__, __loader__", @@ -369,7 +368,8 @@ def _write_stub_file(self, stub_file: str, ext: Extension, compile=False): "", # terminal \n ]) ) - f.close() + with open(stub_file, 'w', encoding="utf-8") as f: + f.write(content) if compile: self._compile_and_remove_stub(stub_file) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 0ca87df357..42a98b919a 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -717,7 +717,7 @@ def gen_setup(self, filename, fragment, tmpdir): shutil.copy2(filename, dst) filename = dst - with open(os.path.join(tmpdir, 'setup.py'), 'w') as file: + with open(os.path.join(tmpdir, 'setup.py'), 'w', encoding="utf-8") as file: file.write( "from setuptools import setup\n" "setup(name=%r, version=%r, py_modules=[%r])\n" From 2675e85a20cff489b9bdce0d958968eca24d542d Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 11:41:45 +0100 Subject: [PATCH 040/135] Use UTF-8 to write metadata files --- setuptools/command/bdist_egg.py | 7 +++---- setuptools/command/easy_install.py | 16 +++++++--------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py index 5581b1d2e0..adcb0a1ba1 100644 --- a/setuptools/command/bdist_egg.py +++ b/setuptools/command/bdist_egg.py @@ -200,10 +200,9 @@ def run(self): # noqa: C901 # is too complex (14) # FIXME log.info("writing %s", native_libs) if not self.dry_run: ensure_directory(native_libs) - libs_file = open(native_libs, 'wt') - libs_file.write('\n'.join(all_outputs)) - libs_file.write('\n') - libs_file.close() + with open(native_libs, 'wt', encoding="utf-8") as libs_file: + libs_file.write('\n'.join(all_outputs)) + libs_file.write('\n') elif os.path.isfile(native_libs): log.info("removing %s", native_libs) if not self.dry_run: diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 3ad984f212..bfacf1e46f 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -1023,12 +1023,11 @@ def install_exe(self, dist_filename, tmpdir): # Write EGG-INFO/PKG-INFO if not os.path.exists(pkg_inf): - f = open(pkg_inf, 'w') # TODO: probably it is safe to use utf-8 - f.write('Metadata-Version: 1.0\n') - for k, v in cfg.items('metadata'): - if k != 'target_version': - f.write('%s: %s\n' % (k.replace('_', '-').title(), v)) - f.close() + with open(pkg_inf, 'w', encoding="utf-8") as f: + f.write('Metadata-Version: 1.0\n') + for k, v in cfg.items('metadata'): + if k != 'target_version': + f.write('%s: %s\n' % (k.replace('_', '-').title(), v)) script_dir = os.path.join(_egg_info, 'scripts') # delete entry-point scripts to avoid duping self.delete_blockers([ @@ -1094,9 +1093,8 @@ def process(src, dst): if locals()[name]: txt = os.path.join(egg_tmp, 'EGG-INFO', name + '.txt') if not os.path.exists(txt): - f = open(txt, 'w') # TODO: probably it is safe to use utf-8 - f.write('\n'.join(locals()[name]) + '\n') - f.close() + with open(txt, 'w', encoding="utf-8") as f: + f.write('\n'.join(locals()[name]) + '\n') def install_wheel(self, wheel_path, tmpdir): wheel = Wheel(wheel_path) From 5305908063aaea823dfc337cb1e5667e7bf2220a Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 11:42:20 +0100 Subject: [PATCH 041/135] Attempt to use UTF-8 to read egg-link files in package_index --- setuptools/package_index.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 42a98b919a..918a34e102 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -422,9 +422,9 @@ def scan_egg_links(self, search_path): list(itertools.starmap(self.scan_egg_link, egg_links)) def scan_egg_link(self, path, entry): - with open(os.path.join(path, entry)) as raw_lines: - # filter non-empty lines - lines = list(filter(None, map(str.strip, raw_lines))) + content = _read_utf8_with_fallback(os.path.join(path, entry)) + # filter non-empty lines + lines = list(filter(None, map(str.strip, content.splitlines()))) if len(lines) != 2: # format is not recognized; punt From f35f9122376186da349ad040dfb1fc59328e52d4 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 11:52:36 +0100 Subject: [PATCH 042/135] Apply ruff formatting --- setuptools/command/build_ext.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py index 49699d30ec..6056fe9b24 100644 --- a/setuptools/command/build_ext.py +++ b/setuptools/command/build_ext.py @@ -342,8 +342,8 @@ def _write_stub_file(self, stub_file: str, ext: Extension, compile=False): if compile and os.path.exists(stub_file): raise BaseError(stub_file + " already exists! Please delete.") if not self.dry_run: - content = ( - '\n'.join([ + with open(stub_file, 'w', encoding="utf-8") as f: + content = '\n'.join([ "def __bootstrap__():", " global __bootstrap__, __file__, __loader__", " import sys, os, pkg_resources, importlib.util" + if_dl(", dl"), @@ -367,8 +367,6 @@ def _write_stub_file(self, stub_file: str, ext: Extension, compile=False): "__bootstrap__()", "", # terminal \n ]) - ) - with open(stub_file, 'w', encoding="utf-8") as f: f.write(content) if compile: self._compile_and_remove_stub(stub_file) From 39a8ef47dbaa5f569cfc327175aa8b74fd572eeb Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Sun, 21 Apr 2024 12:11:16 +0100 Subject: [PATCH 043/135] Simplify conditional encoding in install_scripts and easy_install --- setuptools/command/easy_install.py | 8 ++------ setuptools/command/install_scripts.py | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index bfacf1e46f..41ff382fe4 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -874,12 +874,8 @@ def write_script(self, script_name, contents, mode="t", blockers=()): if os.path.exists(target): os.unlink(target) - if "b" not in mode and isinstance(contents, str): - kw = {"encoding": "utf-8"} - else: - kw = {} - - with open(target, "w" + mode, **kw) as f: + encoding = None if "b" in mode else "utf-8" + with open(target, "w" + mode, encoding=encoding) as f: f.write(contents) chmod(target, 0o777 - mask) diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py index 758937b614..d79a4ab7b0 100644 --- a/setuptools/command/install_scripts.py +++ b/setuptools/command/install_scripts.py @@ -57,14 +57,10 @@ def write_script(self, script_name, contents, mode="t", *ignored): target = os.path.join(self.install_dir, script_name) self.outfiles.append(target) - if "b" not in mode and isinstance(contents, str): - kw = {"encoding": "utf-8"} - else: - kw = {} - + encoding = None if "b" in mode else "utf-8" mask = current_umask() if not self.dry_run: ensure_directory(target) - with open(target, "w" + mode, **kw) as f: + with open(target, "w" + mode, encoding=encoding) as f: f.write(contents) chmod(target, 0o777 - mask) From 0d8c01f15fce24f9ca96f25b093dca2e360bd77b Mon Sep 17 00:00:00 2001 From: Avasam Date: Sun, 21 Apr 2024 16:26:41 -0400 Subject: [PATCH 044/135] Check for self.location is not None in Distribution.activate --- pkg_resources/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 5e24bc9d8e..f8c93fe37b 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2826,7 +2826,7 @@ def activate(self, path=None, replace=False): if path is None: path = sys.path self.insert_on(path, replace=replace) - if path is sys.path: + if path is sys.path and self.location is not None: fixup_namespace_packages(self.location) for pkg in self._get_metadata('namespace_packages.txt'): if pkg in sys.modules: From b8da410f4121c527996ed63affa686f13215a216 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sun, 21 Apr 2024 16:32:00 -0400 Subject: [PATCH 045/135] Fix missing backtick in changelog --- NEWS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.rst b/NEWS.rst index 20c6903a33..73a8148d9c 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -119,7 +119,7 @@ Improved Documentation ---------------------- - Updated documentation referencing obsolete Python 3.7 code. -- by :user:`Avasam` (#4096) -- Changed ``versionadded`` for "Type information included by default" feature from ``v68.3.0`` to ``v69.0.0`` -- by :user:Avasam` (#4182) +- Changed ``versionadded`` for "Type information included by default" feature from ``v68.3.0`` to ``v69.0.0`` -- by :user:`Avasam` (#4182) - Described the auto-generated files -- by :user:`VladimirFokow` (#4198) - Updated "Quickstart" to describe the current status of ``setup.cfg`` and ``pyproject.toml`` -- by :user:`VladimirFokow` (#4200) From 3fbaa4c6d5af1f7846fc21c7fb54952c5cc23621 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 10:31:20 +0100 Subject: [PATCH 046/135] Add deprecation warning for non utf-8 --- pkg_resources/__init__.py | 21 +++++++++++++++++- setuptools/command/setopt.py | 9 ++------ setuptools/package_index.py | 9 ++------ setuptools/unicode_utils.py | 41 ++++++++++++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 5d773da541..675b728f9d 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -3323,14 +3323,33 @@ def _initialize_master_working_set(): globals().update(locals()) -# ---- Ported from ``setuptools`` to avoid introducing dependencies ---- +# ---- Ported from ``setuptools`` to avoid introducing an import inter-dependency ---- LOCALE_ENCODING = "locale" if sys.version_info >= (3, 10) else None def _read_utf8_with_fallback(file: str, fallback_encoding=LOCALE_ENCODING) -> str: + """See setuptools.unicode_utils._read_utf8_with_fallback""" try: with open(file, "r", encoding="utf-8") as f: return f.read() except UnicodeDecodeError: # pragma: no cover + msg = f"""\ + ******************************************************************************** + `encoding="utf-8"` fails with {file!r}, trying `encoding={fallback_encoding!r}`. + + This fallback behaviour is considered **deprecated** and future versions of + `setuptools/pkg_resources` may not implement it. + + Please encode {file!r} with "utf-8" to ensure future builds will succeed. + + If this file was produced by `setuptools` itself, cleaning up the cached files + and re-building/re-installing the package with a newer version of `setuptools` + (e.g. by updating `build-system.requires` in its `pyproject.toml`) + might solve the problem. + ******************************************************************************** + """ + # TODO: Add a deadline? + # See comment in setuptools.unicode_utils._Utf8EncodingNeeded + warnings.warns(msg, PkgResourcesDeprecationWarning, stacklevel=2) with open(file, "r", encoding=fallback_encoding) as f: return f.read() diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index 89b1ac7307..b78d845e60 100644 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -6,7 +6,7 @@ import configparser from .. import Command -from ..compat import py39 +from ..unicode_utils import _cfg_read_utf8_with_fallback __all__ = ['config_file', 'edit_config', 'option_base', 'setopt'] @@ -37,12 +37,7 @@ def edit_config(filename, settings, dry_run=False): log.debug("Reading configuration from %s", filename) opts = configparser.RawConfigParser() opts.optionxform = lambda x: x - - try: - opts.read([filename], encoding="utf-8") - except UnicodeDecodeError: # pragma: no cover - opts.clear() - opts.read([filename], encoding=py39.LOCALE_ENCODING) + _cfg_read_utf8_with_fallback(opts, filename) for section, options in settings.items(): if options is None: diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 918a34e102..f5a7d77eed 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -40,8 +40,7 @@ from setuptools.wheel import Wheel from setuptools.extern.more_itertools import unique_everseen -from .compat import py39 -from .unicode_utils import _read_utf8_with_fallback +from .unicode_utils import _read_utf8_with_fallback, _cfg_read_utf8_with_fallback EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.+!]+)$') @@ -1014,11 +1013,7 @@ def __init__(self): rc = os.path.join(os.path.expanduser('~'), '.pypirc') if os.path.exists(rc): - try: - self.read(rc, encoding="utf-8") - except UnicodeDecodeError: # pragma: no cover - self.clean() - self.read(rc, encoding=py39.LOCALE_ENCODING) + _cfg_read_utf8_with_fallback(self, rc) @property def creds_by_repository(self): diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index 6b60417a91..9934330da9 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -1,7 +1,9 @@ import unicodedata import sys +from configparser import ConfigParser from .compat import py39 +from .warnings import SetuptoolsDeprecationWarning # HFS Plus uses decomposed UTF-8 @@ -57,5 +59,44 @@ def _read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) with open(file, "r", encoding="utf-8") as f: return f.read() except UnicodeDecodeError: # pragma: no cover + _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding) with open(file, "r", encoding=fallback_encoding) as f: return f.read() + + +def _cfg_read_utf8_with_fallback( + cfg: ConfigParser, file: str, fallback_encoding=py39.LOCALE_ENCODING +) -> str: + """Same idea as :func:`_read_utf8_with_fallback`, but for the + :meth:`ConfigParser.read` method. + + This method may call ``cfg.clear()``. + """ + try: + cfg.read(file, encoding="utf-8") + except UnicodeDecodeError: # pragma: no cover + _Utf8EncodingNeeded.emit(file=file, fallback_encoding=fallback_encoding) + cfg.clear() + cfg.read(file, encoding=fallback_encoding) + + +class _Utf8EncodingNeeded(SetuptoolsDeprecationWarning): + _SUMMARY = """ + `encoding="utf-8"` fails with {file!r}, trying `encoding={fallback_encoding!r}`. + """ + + _DETAILS = """ + Fallback behaviour for UTF-8 is considered **deprecated** and future versions of + `setuptools` may not implement it. + + Please encode {file!r} with "utf-8" to ensure future builds will succeed. + + If this file was produced by `setuptools` itself, cleaning up the cached files + and re-building/re-installing the package with a newer version of `setuptools` + (e.g. by updating `build-system.requires` in its `pyproject.toml`) + might solve the problem. + """ + # TODO: Add a deadline? + # Will we be able to remove this? + # The question comes to mind mainly because of sdists that have been produced + # by old versions of setuptools and published to PyPI... From 463b60c8801e7ae1f902bf2f53fa32889b867866 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 10:34:52 +0100 Subject: [PATCH 047/135] Update news fragment to mention deprecation --- newsfragments/4309.removal.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/newsfragments/4309.removal.rst b/newsfragments/4309.removal.rst index 08818104f9..b69b17d45f 100644 --- a/newsfragments/4309.removal.rst +++ b/newsfragments/4309.removal.rst @@ -3,3 +3,5 @@ This change regards mostly files produced and consumed during the build process (e.g. metadata files, script wrappers, automatically updated config files, etc..) Although precautions were taken to minimize disruptions, some edge cases might be subject to backwards incompatibility. + +Support for ``"locale"`` encoding is now **deprecated**. From 1c91ac81823a32d2d7ff55cde8abef7e4ebfc3e3 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 10:39:41 +0100 Subject: [PATCH 048/135] Fix type hint in setuptools/unicode_utils.py --- setuptools/unicode_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/unicode_utils.py b/setuptools/unicode_utils.py index 9934330da9..696b34c46a 100644 --- a/setuptools/unicode_utils.py +++ b/setuptools/unicode_utils.py @@ -66,7 +66,7 @@ def _read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING) def _cfg_read_utf8_with_fallback( cfg: ConfigParser, file: str, fallback_encoding=py39.LOCALE_ENCODING -) -> str: +) -> None: """Same idea as :func:`_read_utf8_with_fallback`, but for the :meth:`ConfigParser.read` method. From 57a29feea3917cad0fc45e5b6148a70b7aab0f5b Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Fri, 19 Apr 2024 04:58:32 +0000 Subject: [PATCH 049/135] Uses RST substitution to put badges in 1 line --- README.rst | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index eec6e35531..181c3b2af6 100644 --- a/README.rst +++ b/README.rst @@ -1,32 +1,34 @@ -.. image:: https://img.shields.io/pypi/v/setuptools.svg +.. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg :target: https://pypi.org/project/setuptools -.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg +.. |py-version| image:: https://img.shields.io/pypi/pyversions/setuptools.svg -.. image:: https://github.com/pypa/setuptools/actions/workflows/main.yml/badge.svg +.. |test-badge| image:: https://github.com/pypa/setuptools/actions/workflows/main.yml/badge.svg :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22 :alt: tests -.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json - :target: https://github.com/astral-sh/ruff - :alt: Ruff +.. |ruff-badge| image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json + :target: https://github.com/astral-sh/ruff + :alt: Ruff -.. image:: https://img.shields.io/readthedocs/setuptools/latest.svg - :target: https://setuptools.pypa.io +.. |docs-badge| image:: https://img.shields.io/readthedocs/setuptools/latest.svg + :target: https://setuptools.pypa.io -.. image:: https://img.shields.io/badge/skeleton-2024-informational +.. |skeleton-badge| image:: https://img.shields.io/badge/skeleton-2024-informational :target: https://blog.jaraco.com/skeleton -.. image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white +.. |codecov-badge| image:: https://img.shields.io/codecov/c/github/pypa/setuptools/master.svg?logo=codecov&logoColor=white :target: https://codecov.io/gh/pypa/setuptools -.. image:: https://tidelift.com/badges/github/pypa/setuptools?style=flat +.. |tidelift-badge| 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 +.. |discord-badge| image:: https://img.shields.io/discord/803025117553754132 :target: https://discord.com/channels/803025117553754132/815945031150993468 :alt: Discord +|pypi-version| |py-version| |test-badge| |ruff-badge| |docs-badge| |skeleton-badge| |codecov-badge| |discord-badge| + See the `Quickstart `_ and the `User's Guide `_ for instructions on how to use Setuptools. From d16f1921723de09bcb61e814c63c78f23631f4ef Mon Sep 17 00:00:00 2001 From: shenxianpeng Date: Fri, 19 Apr 2024 05:04:39 +0000 Subject: [PATCH 050/135] Added a news fragment --- newsfragments/4312.doc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4312.doc.rst diff --git a/newsfragments/4312.doc.rst b/newsfragments/4312.doc.rst new file mode 100644 index 0000000000..7ada954876 --- /dev/null +++ b/newsfragments/4312.doc.rst @@ -0,0 +1 @@ +Uses RST substitution to put badges in 1 line. From a14e36a670ec76fc631eba04f3bbde3b57ef5547 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 13:01:07 +0100 Subject: [PATCH 051/135] Add cog annotations to extern/__init__.py for future checks --- pkg_resources/extern/__init__.py | 7 +++++++ setuptools/extern/__init__.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py index df96f7f26d..12a8fccda1 100644 --- a/pkg_resources/extern/__init__.py +++ b/pkg_resources/extern/__init__.py @@ -70,6 +70,12 @@ def install(self): sys.meta_path.append(self) +# [[[cog +# import cog +# from tools.vendored import yield_root_package +# names = "\n".join(f" {x!r}," for x in yield_root_package('pkg_resources')) +# cog.outl(f"names = (\n{names}\n)") +# ]]] names = ( 'packaging', 'platformdirs', @@ -78,4 +84,5 @@ def install(self): 'more_itertools', 'backports', ) +# [[[end]]] VendorImporter(__name__, names).install() diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py index 427b27cb80..66e216f9b4 100644 --- a/setuptools/extern/__init__.py +++ b/setuptools/extern/__init__.py @@ -70,6 +70,12 @@ def install(self): sys.meta_path.append(self) +# [[[cog +# import cog +# from tools.vendored import yield_root_package +# names = "\n".join(f" {x!r}," for x in yield_root_package('setuptools')) +# cog.outl(f"names = (\n{names}\n)") +# ]]] names = ( 'packaging', 'ordered_set', @@ -82,4 +88,5 @@ def install(self): 'tomli', 'backports', ) +# [[[end]]] VendorImporter(__name__, names, 'setuptools._vendor').install() From a19973b25e46219865310402ee17494ff02a0809 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 13:25:43 +0100 Subject: [PATCH 052/135] Add a function to tools/vendored to list root packages --- tools/vendored.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/vendored.py b/tools/vendored.py index 232e9625d2..685b084134 100644 --- a/tools/vendored.py +++ b/tools/vendored.py @@ -166,4 +166,17 @@ def update_setuptools(): rewrite_more_itertools(vendor / "more_itertools") +def yield_root_package(name): + """Useful when defining the MetaPathFinder + >>> set(yield_root_package("setuptools")) & {"jaraco", "backports"} + {'jaraco', 'backports'} + """ + vendored = Path(f"{name}/_vendor/vendored.txt") + yield from ( + line.partition("=")[0].partition(".")[0].replace("-", "_") + for line in vendored.read_text(encoding="utf-8").splitlines() + if line and not line.startswith("#") + ) + + __name__ == '__main__' and update_vendored() From bf573220152f47d0b90ef6f6e2d90890fb41f564 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 13:28:47 +0100 Subject: [PATCH 053/135] Update tox testenv 'vendor' to use cog to automatically update/check *.extern --- tox.ini | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 7412730008..22dd7af8da 100644 --- a/tox.ini +++ b/tox.ini @@ -69,12 +69,16 @@ pass_env = * commands = python tools/finalize.py -[testenv:vendor] +[testenv:{vendor,check-extern}] skip_install = True +allowlist_externals = sh deps = path + cogapp commands = - python -m tools.vendored + vendor: python -m tools.vendored + vendor: sh -c "git grep -l -F '\[\[\[cog' | xargs cog -I {toxinidir} -r" # update `*.extern` + check-extern: sh -c "git grep -l -F '\[\[\[cog' | xargs cog -I {toxinidir} --check" [testenv:generate-validation-code] skip_install = True From 536ff950a05f7adfd5626d1cf3cdf7bffb3f887e Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 13:39:38 +0100 Subject: [PATCH 054/135] Sync */vendored.txt and *.extern.py --- pkg_resources/_vendor/vendored.txt | 2 ++ pkg_resources/extern/__init__.py | 2 ++ setuptools/extern/__init__.py | 6 +++--- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index c18a2cc0eb..967a2841f3 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -9,5 +9,7 @@ jaraco.text==3.7.0 importlib_resources==5.10.2 # required for importlib_resources on older Pythons zipp==3.7.0 +# required for jaraco.functools +more_itertools==8.8.0 # required for jaraco.context on older Pythons backports.tarfile diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py index 12a8fccda1..7f80b04164 100644 --- a/pkg_resources/extern/__init__.py +++ b/pkg_resources/extern/__init__.py @@ -79,8 +79,10 @@ def install(self): names = ( 'packaging', 'platformdirs', + 'typing_extensions', 'jaraco', 'importlib_resources', + 'zipp', 'more_itertools', 'backports', ) diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py index 66e216f9b4..16e2c9ea9e 100644 --- a/setuptools/extern/__init__.py +++ b/setuptools/extern/__init__.py @@ -80,11 +80,11 @@ def install(self): 'packaging', 'ordered_set', 'more_itertools', - 'importlib_metadata', - 'zipp', - 'importlib_resources', 'jaraco', + 'importlib_resources', + 'importlib_metadata', 'typing_extensions', + 'zipp', 'tomli', 'backports', ) From 35bb574c99b1730d4284b37bd7770c2bf9832e82 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 13:49:20 +0100 Subject: [PATCH 055/135] Add 'check-extern' as collateral to github actions workflow --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3f3b53aa07..6ec4f83be5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -122,6 +122,7 @@ jobs: job: - diffcov - docs + - check-extern runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 From 86770badfb4290cfbdce19880f5590fb33390896 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 13:58:58 +0100 Subject: [PATCH 056/135] Match version of more_itertools that is already installed in pkg_resources with vendored.txt --- pkg_resources/_vendor/vendored.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt index 967a2841f3..f8cbfd967e 100644 --- a/pkg_resources/_vendor/vendored.txt +++ b/pkg_resources/_vendor/vendored.txt @@ -10,6 +10,6 @@ importlib_resources==5.10.2 # required for importlib_resources on older Pythons zipp==3.7.0 # required for jaraco.functools -more_itertools==8.8.0 +more_itertools==10.2.0 # required for jaraco.context on older Pythons backports.tarfile From 175787e1066c865dcaf18e66563a00233eab2c9d Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 14:25:11 +0100 Subject: [PATCH 057/135] Improve determinism in doctest for tools/vendored --- tools/vendored.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/vendored.py b/tools/vendored.py index 685b084134..63797ea24a 100644 --- a/tools/vendored.py +++ b/tools/vendored.py @@ -168,8 +168,9 @@ def update_setuptools(): def yield_root_package(name): """Useful when defining the MetaPathFinder - >>> set(yield_root_package("setuptools")) & {"jaraco", "backports"} - {'jaraco', 'backports'} + >>> examples = set(yield_root_package("setuptools")) & {"jaraco", "backports"} + >>> list(sorted(examples)) + ['backports', 'jaraco'] """ vendored = Path(f"{name}/_vendor/vendored.txt") yield from ( From a30589bf3ce8b94b1b8abbb14e8a470778680950 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Thu, 25 Apr 2024 12:48:42 +0100 Subject: [PATCH 058/135] Avoid errors on Python 3.8 macos-latest as GitHub CI has dropped support --- .github/workflows/main.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3f3b53aa07..d08b857eca 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -61,6 +61,12 @@ jobs: - platform: ubuntu-latest python: "3.10" distutils: stdlib + # Python 3.9 is on macos-13 but not macos-latest (macos-14-arm64) + # https://github.com/actions/setup-python/issues/850 + # https://github.com/actions/setup-python/issues/696#issuecomment-1637587760 + - {python: "3.8", platform: "macos-13"} + exclude: + - {python: "3.8", platform: "macos-latest"} runs-on: ${{ matrix.platform }} continue-on-error: ${{ matrix.python == '3.13' }} env: From 37f20201aec28a61f011a6248f042d174292976d Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Thu, 25 Apr 2024 13:02:57 +0100 Subject: [PATCH 059/135] Add review suggestions. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d08b857eca..82757f478c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -61,7 +61,7 @@ jobs: - platform: ubuntu-latest python: "3.10" distutils: stdlib - # Python 3.9 is on macos-13 but not macos-latest (macos-14-arm64) + # Python 3.8, 3.9 are on macos-13 but not macos-latest (macos-14-arm64) # https://github.com/actions/setup-python/issues/850 # https://github.com/actions/setup-python/issues/696#issuecomment-1637587760 - {python: "3.8", platform: "macos-13"} From b4cfab303aa1f3c2b52ff62da479f321f0681a08 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Thu, 25 Apr 2024 13:34:38 +0100 Subject: [PATCH 060/135] Mark unstable tests on macOS --- setuptools/tests/test_editable_install.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py index 119b128694..91b65e5a38 100644 --- a/setuptools/tests/test_editable_install.py +++ b/setuptools/tests/test_editable_install.py @@ -118,6 +118,7 @@ def editable_opts(request): SETUP_SCRIPT_STUB = "__import__('setuptools').setup()" +@pytest.mark.xfail(sys.platform == "darwin", reason="Test is unstable on macOS?") @pytest.mark.parametrize( "files", [ @@ -897,6 +898,7 @@ class TestOverallBehaviour: }, } + @pytest.mark.xfail(sys.platform == "darwin", reason="Test is unstable on macOS?") @pytest.mark.parametrize("layout", EXAMPLES.keys()) def test_editable_install(self, tmp_path, venv, layout, editable_opts): project, _ = install_project( From f3f5bf7869e87d80eb9457c2e0537f82af255500 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Thu, 25 Apr 2024 13:44:51 +0100 Subject: [PATCH 061/135] Add proper xfail mark --- setuptools/tests/test_editable_install.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py index 91b65e5a38..300a02cfb9 100644 --- a/setuptools/tests/test_editable_install.py +++ b/setuptools/tests/test_editable_install.py @@ -118,7 +118,7 @@ def editable_opts(request): SETUP_SCRIPT_STUB = "__import__('setuptools').setup()" -@pytest.mark.xfail(sys.platform == "darwin", reason="Test is unstable on macOS?") +@pytest.mark.xfail(sys.platform == "darwin", reason="pypa/setuptools#4328") @pytest.mark.parametrize( "files", [ @@ -898,7 +898,7 @@ class TestOverallBehaviour: }, } - @pytest.mark.xfail(sys.platform == "darwin", reason="Test is unstable on macOS?") + @pytest.mark.xfail(sys.platform == "darwin", reason="pypa/setuptools#4328") @pytest.mark.parametrize("layout", EXAMPLES.keys()) def test_editable_install(self, tmp_path, venv, layout, editable_opts): project, _ = install_project( From 80d101eea16a6fc72759e193882ef60451f51d98 Mon Sep 17 00:00:00 2001 From: Avasam Date: Fri, 8 Mar 2024 17:21:56 -0500 Subject: [PATCH 062/135] Update `pytest.ini` for `EncodingWarning` from external libraries + avoid getpreferredencoding when possible --- pytest.ini | 27 ++++++++++++++------------- setuptools/command/editable_wheel.py | 3 ++- setuptools/tests/__init__.py | 9 +++++++-- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/pytest.ini b/pytest.ini index e7c96274a3..40a64b5cd4 100644 --- a/pytest.ini +++ b/pytest.ini @@ -10,6 +10,18 @@ filterwarnings= # Fail on warnings error + # Workarounds for pypa/setuptools#3810 + # Can't use EncodingWarning as it doesn't exist on Python 3.9. + # These warnings only appear on Python 3.10+ + default:'encoding' argument not specified + + # pypa/distutils#236 + ignore:'encoding' argument not specified::distutils + ignore:'encoding' argument not specified::setuptools._distutils + + # subprocess.check_output still warns with EncodingWarning even with encoding set + ignore:'encoding' argument not specified::setuptools.tests.environment + ## upstream # Ensure ResourceWarnings are emitted @@ -18,14 +30,8 @@ filterwarnings= # realpython/pytest-mypy#152 ignore:'encoding' argument not specified::pytest_mypy - # python/cpython#100750 - ignore:'encoding' argument not specified::platform - - # pypa/build#615 - ignore:'encoding' argument not specified::build.env - - # dateutil/dateutil#1284 - ignore:datetime.datetime.utcfromtimestamp:DeprecationWarning:dateutil.tz.tz + # pytest-dev/pytest # TODO: Raise issue upstream + ignore:'encoding' argument not specified::_pytest ## end upstream @@ -69,11 +75,6 @@ filterwarnings= # https://github.com/pypa/setuptools/issues/3655 ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning - # Workarounds for pypa/setuptools#3810 - # Can't use EncodingWarning as it doesn't exist on Python 3.9 - default:'encoding' argument not specified - default:UTF-8 Mode affects locale.getpreferredencoding(). - # Avoid errors when testing pkg_resources.declare_namespace ignore:.*pkg_resources\.declare_namespace.*:DeprecationWarning diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index 1722817f82..b8ed84750a 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -565,7 +565,8 @@ def _encode_pth(content: str) -> bytes: This function tries to simulate this behaviour without having to create an actual file, in a way that supports a range of active Python versions. (There seems to be some variety in the way different version of Python handle - ``encoding=None``, not all of them use ``locale.getpreferredencoding(False)``). + ``encoding=None``, not all of them use ``locale.getpreferredencoding(False)`` + or ``locale.getencoding()``). """ with io.BytesIO() as buffer: wrapper = io.TextIOWrapper(buffer, encoding=py39.LOCALE_ENCODING) diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index 564adf2b0a..738ebf43be 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -1,10 +1,15 @@ import locale +import sys import pytest __all__ = ['fail_on_ascii'] - -is_ascii = locale.getpreferredencoding() == 'ANSI_X3.4-1968' +locale_encoding = ( + locale.getencoding() + if sys.version_info >= (3, 11) + else locale.getpreferredencoding(False) +) +is_ascii = locale_encoding == 'ANSI_X3.4-1968' fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale") From 9acea31d0e8c3f94db7dca3fedaa256d0f8a2250 Mon Sep 17 00:00:00 2001 From: Avasam Date: Sun, 21 Apr 2024 16:18:38 -0400 Subject: [PATCH 063/135] Remove distutils EncodingWarning exclusion in pytest.ini Vendored distutils was updated with fixes --- pytest.ini | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 40a64b5cd4..2ce6e3e1e7 100644 --- a/pytest.ini +++ b/pytest.ini @@ -15,10 +15,6 @@ filterwarnings= # These warnings only appear on Python 3.10+ default:'encoding' argument not specified - # pypa/distutils#236 - ignore:'encoding' argument not specified::distutils - ignore:'encoding' argument not specified::setuptools._distutils - # subprocess.check_output still warns with EncodingWarning even with encoding set ignore:'encoding' argument not specified::setuptools.tests.environment From 27f5e0ae4ba6fae8a30bc7b7aa674f8be2afc22f Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 16:34:43 +0100 Subject: [PATCH 064/135] Ignore encoding warnings bu only in in stdlib's distutils --- pytest.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytest.ini b/pytest.ini index 2ce6e3e1e7..648b145b69 100644 --- a/pytest.ini +++ b/pytest.ini @@ -29,6 +29,9 @@ filterwarnings= # pytest-dev/pytest # TODO: Raise issue upstream ignore:'encoding' argument not specified::_pytest + # Already fixed in pypa/distutils, but present in stdlib + ignore:'encoding' argument not specified::distutils + ## end upstream # https://github.com/pypa/setuptools/issues/1823 From ef7d2590ec6b4e4a410b7e4f983386ae07f13f64 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 14:36:08 +0100 Subject: [PATCH 065/135] Remove EncodingWarning workarounds for setuptools from pytest.ini --- pytest.ini | 8 -------- 1 file changed, 8 deletions(-) diff --git a/pytest.ini b/pytest.ini index 648b145b69..1b565222e2 100644 --- a/pytest.ini +++ b/pytest.ini @@ -10,14 +10,6 @@ filterwarnings= # Fail on warnings error - # Workarounds for pypa/setuptools#3810 - # Can't use EncodingWarning as it doesn't exist on Python 3.9. - # These warnings only appear on Python 3.10+ - default:'encoding' argument not specified - - # subprocess.check_output still warns with EncodingWarning even with encoding set - ignore:'encoding' argument not specified::setuptools.tests.environment - ## upstream # Ensure ResourceWarnings are emitted From 4fc0b15d424ae97663d48a34ee7bd586b3aade69 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 15:56:28 +0100 Subject: [PATCH 066/135] Fix EncodingWarning in test_build_meta --- setuptools/tests/test_build_meta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/tests/test_build_meta.py b/setuptools/tests/test_build_meta.py index 43830feb77..cc996b4255 100644 --- a/setuptools/tests/test_build_meta.py +++ b/setuptools/tests/test_build_meta.py @@ -160,7 +160,7 @@ def run(): # to obtain a distribution object first, and then run the distutils # commands later, because these files will be removed in the meantime. - with open('world.py', 'w') as f: + with open('world.py', 'w', encoding="utf-8") as f: f.write('x = 42') try: From d7ac06f0d5892183c9ac9ce5501d785348b4bbde Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 21:48:32 +0100 Subject: [PATCH 067/135] Return comment to pytest.ini that got lost in changes --- pytest.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pytest.ini b/pytest.ini index 1b565222e2..2aceea2d58 100644 --- a/pytest.ini +++ b/pytest.ini @@ -10,6 +10,9 @@ filterwarnings= # Fail on warnings error + # Workarounds for pypa/setuptools#3810 + # Can't use EncodingWarning as it doesn't exist on Python 3.9. + # These warnings only appear on Python 3.10+ ## upstream # Ensure ResourceWarnings are emitted From 969f00b16d190567ce27d9788e90ab4806a32443 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 22:54:45 +0100 Subject: [PATCH 068/135] Re-enable warning filter for distutils.text_file inside test_excluded_subpackages --- setuptools/tests/test_build_py.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py index 4aa1fe68fa..db2052a586 100644 --- a/setuptools/tests/test_build_py.py +++ b/setuptools/tests/test_build_py.py @@ -1,6 +1,7 @@ import os import stat import shutil +import warnings from pathlib import Path from unittest.mock import Mock @@ -162,11 +163,23 @@ def test_excluded_subpackages(tmpdir_cwd): dist.parse_config_files() build_py = dist.get_command_obj("build_py") + msg = r"Python recognizes 'mypkg\.tests' as an importable package" with pytest.warns(SetuptoolsDeprecationWarning, match=msg): # TODO: To fix #3260 we need some transition period to deprecate the # existing behavior of `include_package_data`. After the transition, we # should remove the warning and fix the behaviour. + + if os.getenv("SETUPTOOLS_USE_DISTUTILS") == "stdlib": + # pytest.warns reset the warning filter temporarily + # https://github.com/pytest-dev/pytest/issues/4011#issuecomment-423494810 + warnings.filterwarnings( + "ignore", + "'encoding' argument not specified", + module="distutils.text_file", + # This warning is already fixed in pypa/distutils but not in stdlib + ) + build_py.finalize_options() build_py.run() From 919e3934c9e7a8085bd4ee72d3259be76fe0186b Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 23 Apr 2024 00:02:06 +0100 Subject: [PATCH 069/135] Attempt to fix errors in mypy for PyPy (test of hypothesis) --- pytest.ini | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pytest.ini b/pytest.ini index 2aceea2d58..4bebae0fd9 100644 --- a/pytest.ini +++ b/pytest.ini @@ -18,6 +18,12 @@ filterwarnings= # Ensure ResourceWarnings are emitted default::ResourceWarning + # python/mypy#17057 + ignore:'encoding' argument not specified::mypy.config_parser + ignore:'encoding' argument not specified::mypy.build + ignore:'encoding' argument not specified::mypy.modulefinder + ignore:'encoding' argument not specified::mypy.metastore + # realpython/pytest-mypy#152 ignore:'encoding' argument not specified::pytest_mypy From 57ea91b448ca3852c33a2b164b5d6bdc3551a3a6 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 24 Apr 2024 16:34:19 +0100 Subject: [PATCH 070/135] Attempt to solve the problem in PyPy --- pytest.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pytest.ini b/pytest.ini index 4bebae0fd9..8ab6d5ebf1 100644 --- a/pytest.ini +++ b/pytest.ini @@ -19,10 +19,10 @@ filterwarnings= default::ResourceWarning # python/mypy#17057 - ignore:'encoding' argument not specified::mypy.config_parser - ignore:'encoding' argument not specified::mypy.build - ignore:'encoding' argument not specified::mypy.modulefinder - ignore:'encoding' argument not specified::mypy.metastore + ignore:'encoding' argument not specified::mypy + ignore:'encoding' argument not specified::configparser + # ^-- ConfigParser is called by mypy, + # but ignoring the warning in `mypy` is not enough on PyPy # realpython/pytest-mypy#152 ignore:'encoding' argument not specified::pytest_mypy From 1316a611f2faf5828f3d10a1bb4df7a2475ebeef Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 24 Apr 2024 16:35:03 +0100 Subject: [PATCH 071/135] Better wording for comment in pytest.ini --- pytest.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 8ab6d5ebf1..000e663471 100644 --- a/pytest.ini +++ b/pytest.ini @@ -22,7 +22,8 @@ filterwarnings= ignore:'encoding' argument not specified::mypy ignore:'encoding' argument not specified::configparser # ^-- ConfigParser is called by mypy, - # but ignoring the warning in `mypy` is not enough on PyPy + # but ignoring the warning in `mypy` is not enough + # to make it work on PyPy # realpython/pytest-mypy#152 ignore:'encoding' argument not specified::pytest_mypy From 8dc50ccc2fe55a3c6b2c99f5c95597242c61572d Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 24 Apr 2024 14:02:04 -0400 Subject: [PATCH 072/135] Add newsfragment --- newsfragments/4255.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4255.misc.rst diff --git a/newsfragments/4255.misc.rst b/newsfragments/4255.misc.rst new file mode 100644 index 0000000000..1f9fde768b --- /dev/null +++ b/newsfragments/4255.misc.rst @@ -0,0 +1 @@ +Treat `EncodingWarning`s as an errors in tests. -- by :user:`Avasam` From b69c0de234a9b648828e5f3a180639f5ea0e24fa Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 24 Apr 2024 14:05:12 -0400 Subject: [PATCH 073/135] Update comment --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 000e663471..87e3d9aae3 100644 --- a/pytest.ini +++ b/pytest.ini @@ -28,7 +28,7 @@ filterwarnings= # realpython/pytest-mypy#152 ignore:'encoding' argument not specified::pytest_mypy - # pytest-dev/pytest # TODO: Raise issue upstream + # TODO: Set encoding when openning tmpdir files with pytest's LocalPath.open ignore:'encoding' argument not specified::_pytest # Already fixed in pypa/distutils, but present in stdlib From b341011f3b8be7e10d933831750aed48b381f382 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 24 Apr 2024 14:38:00 -0400 Subject: [PATCH 074/135] Fix Windows issue --- setuptools/tests/test_windows_wrappers.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/setuptools/tests/test_windows_wrappers.py b/setuptools/tests/test_windows_wrappers.py index 3f321386f1..b272689351 100644 --- a/setuptools/tests/test_windows_wrappers.py +++ b/setuptools/tests/test_windows_wrappers.py @@ -110,7 +110,11 @@ def test_basic(self, tmpdir): 'arg5 a\\\\b', ] proc = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, text=True + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True, + encoding="utf-8", ) stdout, stderr = proc.communicate('hello\nworld\n') actual = stdout.replace('\r\n', '\n') @@ -143,7 +147,11 @@ def test_symlink(self, tmpdir): 'arg5 a\\\\b', ] proc = subprocess.Popen( - cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, text=True + cmd, + stdout=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True, + encoding="utf-8", ) stdout, stderr = proc.communicate('hello\nworld\n') actual = stdout.replace('\r\n', '\n') @@ -191,6 +199,7 @@ def test_with_options(self, tmpdir): stdin=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + encoding="utf-8", ) stdout, stderr = proc.communicate() actual = stdout.replace('\r\n', '\n') @@ -240,6 +249,7 @@ def test_basic(self, tmpdir): stdin=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, + encoding="utf-8", ) stdout, stderr = proc.communicate() assert not stdout From 22ca7e5ba90cce639e241428226279b0c7be2242 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Thu, 25 Apr 2024 12:29:09 +0100 Subject: [PATCH 075/135] Update comment in pytest.ini --- pytest.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index 87e3d9aae3..0c9651d96f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -13,6 +13,7 @@ filterwarnings= # Workarounds for pypa/setuptools#3810 # Can't use EncodingWarning as it doesn't exist on Python 3.9. # These warnings only appear on Python 3.10+ + ## upstream # Ensure ResourceWarnings are emitted @@ -28,7 +29,8 @@ filterwarnings= # realpython/pytest-mypy#152 ignore:'encoding' argument not specified::pytest_mypy - # TODO: Set encoding when openning tmpdir files with pytest's LocalPath.open + # TODO: Set encoding when openning/writing tmpdir files with pytest's LocalPath.open + # see pypa/setuptools#4326 ignore:'encoding' argument not specified::_pytest # Already fixed in pypa/distutils, but present in stdlib From 3ea4aa933ba140cb1c19ce44dfef4564563a79ac Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Thu, 25 Apr 2024 14:40:49 +0100 Subject: [PATCH 076/135] Improve RST syntax on news fragment. --- newsfragments/4255.misc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newsfragments/4255.misc.rst b/newsfragments/4255.misc.rst index 1f9fde768b..e5e5728d70 100644 --- a/newsfragments/4255.misc.rst +++ b/newsfragments/4255.misc.rst @@ -1 +1 @@ -Treat `EncodingWarning`s as an errors in tests. -- by :user:`Avasam` +Treat ``EncodingWarning``s as an errors in tests. -- by :user:`Avasam` From 0faba5086564eab2feb2fa94f2e4592c50589952 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 25 Apr 2024 10:05:44 -0400 Subject: [PATCH 077/135] Fix typo --- newsfragments/4255.misc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/newsfragments/4255.misc.rst b/newsfragments/4255.misc.rst index e5e5728d70..50a0a3d195 100644 --- a/newsfragments/4255.misc.rst +++ b/newsfragments/4255.misc.rst @@ -1 +1 @@ -Treat ``EncodingWarning``s as an errors in tests. -- by :user:`Avasam` +Treat ``EncodingWarning``s as errors in tests. -- by :user:`Avasam` From 7b17049aabb7b493ad106fcefe00856607b6f181 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 15:24:49 -0400 Subject: [PATCH 078/135] Pin against pyproject-hooks==1.1. Closes #4333. --- .github/workflows/main.yml | 3 ++- setup.cfg | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c5aed1d1a0..ec2e567a1e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -95,7 +95,8 @@ jobs: shell: bash run: | rm -rf dist - pipx run build + # workaround for pypa/setuptools#4333 + pipx run --pip-args 'pyproject-hooks!=1.1' build echo "PRE_BUILT_SETUPTOOLS_SDIST=$(ls dist/*.tar.gz)" >> $GITHUB_ENV echo "PRE_BUILT_SETUPTOOLS_WHEEL=$(ls dist/*.whl)" >> $GITHUB_ENV rm -rf setuptools.egg-info # Avoid interfering with the other tests diff --git a/setup.cfg b/setup.cfg index c8bb0ed41d..68be6c8e7c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -77,6 +77,9 @@ testing = # No Python 3.12 dependencies require importlib_metadata, but needed for type-checking since we import it directly importlib_metadata + # workaround for pypa/setuptools#4333 + pyproject-hooks!=1.1 + docs = # upstream sphinx >= 3.5 From 4a0a9ce587515edce83ab97aa5c7943c045ac180 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 16:13:58 -0400 Subject: [PATCH 079/135] Make the test less fragile and search simply for the presence of a ValueError in the traceback. Closes #4334. --- setuptools/tests/test_egg_info.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index a4b0ecf398..f6b2302d97 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -213,13 +213,9 @@ def test_license_is_a_string(self, tmpdir_cwd, env): with pytest.raises(AssertionError) as exc: self._run_egg_info_command(tmpdir_cwd, env) - # Hopefully this is not too fragile: the only argument to the - # assertion error should be a traceback, ending with: - # ValueError: .... - # - # assert not 1 - tb = exc.value.args[0].split('\n') - assert tb[-3].lstrip().startswith('ValueError') + # The only argument to the assertion error should be a traceback + # containing a ValueError + assert 'ValueError' in exc.value.args[0] def test_rebuilt(self, tmpdir_cwd, env): """Ensure timestamps are updated when the command is re-run.""" From fe8980b4505cea1982979fdca20c4078ed8fb8c6 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 09:38:31 -0400 Subject: [PATCH 080/135] Remove pop_prefix parameter, unused. --- setuptools/package_index.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index f5a7d77eed..345344c2c2 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -863,7 +863,7 @@ def _download_svn(self, url, _filename): raise DistutilsError(f"Invalid config, SVN download is not supported: {url}") @staticmethod - def _vcs_split_rev_from_url(url, pop_prefix=False): + def _vcs_split_rev_from_url(url): scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) scheme = scheme.split('+', 1)[-1] @@ -882,7 +882,7 @@ def _vcs_split_rev_from_url(url, pop_prefix=False): def _download_git(self, url, filename): filename = filename.split('#', 1)[0] - url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) + url, rev = self._vcs_split_rev_from_url(url) self.info("Doing git clone from %s to %s", url, filename) os.system("git clone --quiet %s %s" % (url, filename)) @@ -901,7 +901,7 @@ def _download_git(self, url, filename): def _download_hg(self, url, filename): filename = filename.split('#', 1)[0] - url, rev = self._vcs_split_rev_from_url(url, pop_prefix=True) + url, rev = self._vcs_split_rev_from_url(url) self.info("Doing hg clone from %s to %s", url, filename) os.system("hg clone --quiet %s %s" % (url, filename)) From 35ee2b4abd8bc745766c809d31fc6bf19e6979dc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 09:49:56 -0400 Subject: [PATCH 081/135] Add a test capturing the basic expectation. --- setuptools/package_index.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 345344c2c2..d2985cc1f9 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -864,6 +864,15 @@ def _download_svn(self, url, _filename): @staticmethod def _vcs_split_rev_from_url(url): + """ + >>> vsrfu = PackageIndex._vcs_split_rev_from_url + >>> vsrfu('git+https://github.com/pypa/setuptools@v69.0.0#egg-info=setuptools') + ('https://github.com/pypa/setuptools', 'v69.0.0') + >>> vsrfu('git+https://github.com/pypa/setuptools#egg-info=setuptools') + ('https://github.com/pypa/setuptools', None) + >>> vsrfu('http://foo/bar') + ('http://foo/bar', None) + """ scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) scheme = scheme.split('+', 1)[-1] From eb42e5c45b5888863aa1877517f6dbf6f7b080cc Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 10:15:31 -0400 Subject: [PATCH 082/135] Update _vcs_split_rev_from_url to use modern constructs. --- setuptools/package_index.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index d2985cc1f9..9da138d87c 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -865,6 +865,8 @@ def _download_svn(self, url, _filename): @staticmethod def _vcs_split_rev_from_url(url): """ + Given a possible VCS URL, return a clean URL and resolved revision if any. + >>> vsrfu = PackageIndex._vcs_split_rev_from_url >>> vsrfu('git+https://github.com/pypa/setuptools@v69.0.0#egg-info=setuptools') ('https://github.com/pypa/setuptools', 'v69.0.0') @@ -873,21 +875,24 @@ def _vcs_split_rev_from_url(url): >>> vsrfu('http://foo/bar') ('http://foo/bar', None) """ - scheme, netloc, path, query, frag = urllib.parse.urlsplit(url) + parts = urllib.parse.urlsplit(url) - scheme = scheme.split('+', 1)[-1] + clean_scheme = parts.scheme.split('+', 1)[-1] # Some fragment identification fails - path = path.split('#', 1)[0] + no_fragment_path, _, _ = parts.path.partition('#') - rev = None - if '@' in path: - path, rev = path.rsplit('@', 1) + pre, sep, post = no_fragment_path.rpartition('@') + clean_path, rev = (pre, post) if sep else (post, None) - # Also, discard fragment - url = urllib.parse.urlunsplit((scheme, netloc, path, query, '')) + resolved = parts._replace( + scheme=clean_scheme, + path=clean_path, + # discard the fragment + fragment='', + ).geturl() - return url, rev + return resolved, rev def _download_git(self, url, filename): filename = filename.split('#', 1)[0] From 7c1c29b56dcff03bb637eeabba139c31600a55d1 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 10:25:24 -0400 Subject: [PATCH 083/135] package-index: Extract fall-through methods _download_vcs and _download_other. --- setuptools/package_index.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 9da138d87c..5a3e9db2a2 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -831,19 +831,24 @@ def _download_url(self, scheme, url, tmpdir): filename = os.path.join(tmpdir, name) - # Download the file - # + return self._download_vcs(url, filename) or self._download_other(url, filename) + + def _download_vcs(self, url, filename): + scheme = urllib.parse.urlsplit(url).scheme if scheme == 'svn' or scheme.startswith('svn+'): return self._download_svn(url, filename) elif scheme == 'git' or scheme.startswith('git+'): return self._download_git(url, filename) elif scheme.startswith('hg+'): return self._download_hg(url, filename) - elif scheme == 'file': - return urllib.request.url2pathname(urllib.parse.urlparse(url)[2]) - else: - self.url_ok(url, True) # raises error if not allowed - return self._attempt_download(url, filename) + + def _download_other(self, url, filename): + scheme = urllib.parse.urlsplit(url).scheme + if scheme == 'file': + return urllib.request.url2pathname(urllib.parse.urlparse(url).path) + # raise error if not allowed + self.url_ok(url, True) + return self._attempt_download(url, filename) def scan_url(self, url): self.process_url(url, True) From 4d54fa77943c78f393217b2931665d3ab64cd3f2 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 10:46:34 -0400 Subject: [PATCH 084/135] Extract _resolve_vcs for resolving a VCS from a URL. --- setuptools/package_index.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 5a3e9db2a2..8d46a70c47 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1,5 +1,6 @@ """PyPI and direct package downloading.""" +import contextlib import sys import os import re @@ -587,7 +588,7 @@ def download(self, spec, tmpdir): scheme = URL_SCHEME(spec) if scheme: # It's a url, download it to tmpdir - found = self._download_url(scheme.group(1), spec, tmpdir) + found = self._download_url(spec, tmpdir) base, fragment = egg_info_for_url(spec) if base.endswith('.py'): found = self.gen_setup(found, fragment, tmpdir) @@ -816,7 +817,7 @@ def open_url(self, url, warning=None): # noqa: C901 # is too complex (12) else: raise DistutilsError("Download error for %s: %s" % (url, v)) from v - def _download_url(self, scheme, url, tmpdir): + def _download_url(self, url, tmpdir): # Determine download filename # name, fragment = egg_info_for_url(url) @@ -833,14 +834,29 @@ def _download_url(self, scheme, url, tmpdir): return self._download_vcs(url, filename) or self._download_other(url, filename) - def _download_vcs(self, url, filename): + @staticmethod + def _resolve_vcs(url): + """ + >>> rvcs = PackageIndex._resolve_vcs + >>> rvcs('git+http://foo/bar') + 'git' + >>> rvcs('hg+https://foo/bar') + 'hg' + >>> rvcs('git:myhost') + 'git' + >>> rvcs('hg:myhost') + >>> rvcs('http://foo/bar') + """ scheme = urllib.parse.urlsplit(url).scheme - if scheme == 'svn' or scheme.startswith('svn+'): - return self._download_svn(url, filename) - elif scheme == 'git' or scheme.startswith('git+'): - return self._download_git(url, filename) - elif scheme.startswith('hg+'): - return self._download_hg(url, filename) + pre, sep, post = scheme.partition('+') + # svn and git have their own protocol; hg does not + allowed = set(['svn', 'git'] + ['hg'] * bool(sep)) + return next(iter({pre} & allowed), None) + + def _download_vcs(self, url, filename): + vcs = self._resolve_vcs(url) + with contextlib.suppress(AttributeError): + return getattr(self, f'_download_{vcs}')(url, filename) def _download_other(self, url, filename): scheme = urllib.parse.urlsplit(url).scheme From cf18f716c1fe638d812d487f61aa987101a763a8 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 10:55:48 -0400 Subject: [PATCH 085/135] Consolidated all _download_vcs methods into one. --- setuptools/package_index.py | 68 +++++++++++++------------------------ 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 8d46a70c47..ba7819304f 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1,6 +1,5 @@ """PyPI and direct package downloading.""" -import contextlib import sys import os import re @@ -853,10 +852,30 @@ def _resolve_vcs(url): allowed = set(['svn', 'git'] + ['hg'] * bool(sep)) return next(iter({pre} & allowed), None) - def _download_vcs(self, url, filename): + def _download_vcs(self, url, spec_filename): vcs = self._resolve_vcs(url) - with contextlib.suppress(AttributeError): - return getattr(self, f'_download_{vcs}')(url, filename) + if not vcs: + return + if vcs == 'svn': + raise DistutilsError( + f"Invalid config, SVN download is not supported: {url}" + ) + + filename, _, _ = spec_filename.partition('#') + url, rev = self._vcs_split_rev_from_url(url) + + self.info(f"Doing {vcs} clone from {url} to {filename}") + os.system(f"{vcs} clone --quiet {url} {filename}") + + co_commands = dict( + git=f"git -C {filename} checkout --quiet {rev}", + hg=f"hg --cwd {filename} up -C -r {rev} -q", + ) + if rev is not None: + self.info(f"Checking out {rev}") + os.system(co_commands[vcs]) + + return filename def _download_other(self, url, filename): scheme = urllib.parse.urlsplit(url).scheme @@ -880,9 +899,6 @@ def _invalid_download_html(self, url, headers, filename): os.unlink(filename) raise DistutilsError(f"Unexpected HTML page found at {url}") - def _download_svn(self, url, _filename): - raise DistutilsError(f"Invalid config, SVN download is not supported: {url}") - @staticmethod def _vcs_split_rev_from_url(url): """ @@ -915,44 +931,6 @@ def _vcs_split_rev_from_url(url): return resolved, rev - def _download_git(self, url, filename): - filename = filename.split('#', 1)[0] - url, rev = self._vcs_split_rev_from_url(url) - - self.info("Doing git clone from %s to %s", url, filename) - os.system("git clone --quiet %s %s" % (url, filename)) - - if rev is not None: - self.info("Checking out %s", rev) - os.system( - "git -C %s checkout --quiet %s" - % ( - filename, - rev, - ) - ) - - return filename - - def _download_hg(self, url, filename): - filename = filename.split('#', 1)[0] - url, rev = self._vcs_split_rev_from_url(url) - - self.info("Doing hg clone from %s to %s", url, filename) - os.system("hg clone --quiet %s %s" % (url, filename)) - - if rev is not None: - self.info("Updating to %s", rev) - os.system( - "hg --cwd %s up -C -r %s -q" - % ( - filename, - rev, - ) - ) - - return filename - def debug(self, msg, *args): log.debug(msg, *args) From f0cda0b9a3cf9d81844738ba96b1df95e2abb799 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 11:40:10 -0400 Subject: [PATCH 086/135] Replace os.system calls with subprocess calls. --- setup.cfg | 1 + setuptools/package_index.py | 9 +++--- setuptools/tests/test_packageindex.py | 46 ++++++++++++--------------- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/setup.cfg b/setup.cfg index 68be6c8e7c..1226c940fc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -76,6 +76,7 @@ testing = tomli # No Python 3.12 dependencies require importlib_metadata, but needed for type-checking since we import it directly importlib_metadata + pytest-subprocess # workaround for pypa/setuptools#4333 pyproject-hooks!=1.1 diff --git a/setuptools/package_index.py b/setuptools/package_index.py index ba7819304f..bbc5846ed9 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -1,6 +1,7 @@ """PyPI and direct package downloading.""" import sys +import subprocess import os import re import io @@ -865,15 +866,15 @@ def _download_vcs(self, url, spec_filename): url, rev = self._vcs_split_rev_from_url(url) self.info(f"Doing {vcs} clone from {url} to {filename}") - os.system(f"{vcs} clone --quiet {url} {filename}") + subprocess.check_call([vcs, 'clone', '--quiet', url, filename]) co_commands = dict( - git=f"git -C {filename} checkout --quiet {rev}", - hg=f"hg --cwd {filename} up -C -r {rev} -q", + git=[vcs, '-C', filename, 'checkout', '--quiet', rev], + hg=[vcs, '--cwd', filename, 'up', '-C', '-r', rev, '-q'], ) if rev is not None: self.info(f"Checking out {rev}") - os.system(co_commands[vcs]) + subprocess.check_call(co_commands[vcs]) return filename diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 93474ae5af..2776ba9f63 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -3,7 +3,6 @@ import urllib.error import http.client from inspect import cleandoc -from unittest import mock import pytest @@ -171,41 +170,38 @@ def test_egg_fragment(self): assert dists[0].version == '' assert dists[1].version == vc - def test_download_git_with_rev(self, tmpdir): + def test_download_git_with_rev(self, tmpdir, fp): url = 'git+https://github.example/group/project@master#egg=foo' index = setuptools.package_index.PackageIndex() - with mock.patch("os.system") as os_system_mock: - result = index.download(url, str(tmpdir)) + expected_dir = str(tmpdir / 'project@master') + fp.register([ + 'git', + 'clone', + '--quiet', + 'https://github.example/group/project', + expected_dir, + ]) + fp.register(['git', '-C', expected_dir, 'checkout', '--quiet', 'master']) - os_system_mock.assert_called() + result = index.download(url, str(tmpdir)) - expected_dir = str(tmpdir / 'project@master') - expected = ( - 'git clone --quiet ' 'https://github.example/group/project {expected_dir}' - ).format(**locals()) - first_call_args = os_system_mock.call_args_list[0][0] - assert first_call_args == (expected,) - - tmpl = 'git -C {expected_dir} checkout --quiet master' - expected = tmpl.format(**locals()) - assert os_system_mock.call_args_list[1][0] == (expected,) assert result == expected_dir + assert len(fp.calls) == 2 - def test_download_git_no_rev(self, tmpdir): + def test_download_git_no_rev(self, tmpdir, fp): url = 'git+https://github.example/group/project#egg=foo' index = setuptools.package_index.PackageIndex() - with mock.patch("os.system") as os_system_mock: - result = index.download(url, str(tmpdir)) - - os_system_mock.assert_called() - expected_dir = str(tmpdir / 'project') - expected = ( - 'git clone --quiet ' 'https://github.example/group/project {expected_dir}' - ).format(**locals()) - os_system_mock.assert_called_once_with(expected) + fp.register([ + 'git', + 'clone', + '--quiet', + 'https://github.example/group/project', + expected_dir, + ]) + index.download(url, str(tmpdir)) def test_download_svn(self, tmpdir): url = 'svn+https://svn.example/project#egg=foo' From a36b1121d817ef82aef0971aaa37989941019c8f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 11:55:20 -0400 Subject: [PATCH 087/135] Prefer tmp_path fixture. --- setuptools/tests/test_packageindex.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/setuptools/tests/test_packageindex.py b/setuptools/tests/test_packageindex.py index 2776ba9f63..f5f37e0563 100644 --- a/setuptools/tests/test_packageindex.py +++ b/setuptools/tests/test_packageindex.py @@ -170,11 +170,11 @@ def test_egg_fragment(self): assert dists[0].version == '' assert dists[1].version == vc - def test_download_git_with_rev(self, tmpdir, fp): + def test_download_git_with_rev(self, tmp_path, fp): url = 'git+https://github.example/group/project@master#egg=foo' index = setuptools.package_index.PackageIndex() - expected_dir = str(tmpdir / 'project@master') + expected_dir = tmp_path / 'project@master' fp.register([ 'git', 'clone', @@ -184,16 +184,16 @@ def test_download_git_with_rev(self, tmpdir, fp): ]) fp.register(['git', '-C', expected_dir, 'checkout', '--quiet', 'master']) - result = index.download(url, str(tmpdir)) + result = index.download(url, tmp_path) - assert result == expected_dir + assert result == str(expected_dir) assert len(fp.calls) == 2 - def test_download_git_no_rev(self, tmpdir, fp): + def test_download_git_no_rev(self, tmp_path, fp): url = 'git+https://github.example/group/project#egg=foo' index = setuptools.package_index.PackageIndex() - expected_dir = str(tmpdir / 'project') + expected_dir = tmp_path / 'project' fp.register([ 'git', 'clone', @@ -201,15 +201,15 @@ def test_download_git_no_rev(self, tmpdir, fp): 'https://github.example/group/project', expected_dir, ]) - index.download(url, str(tmpdir)) + index.download(url, tmp_path) - def test_download_svn(self, tmpdir): + def test_download_svn(self, tmp_path): url = 'svn+https://svn.example/project#egg=foo' index = setuptools.package_index.PackageIndex() msg = r".*SVN download is not supported.*" with pytest.raises(distutils.errors.DistutilsError, match=msg): - index.download(url, str(tmpdir)) + index.download(url, tmp_path) class TestContentCheckers: From 1a0cbf5f59b5fa1debb4b46fd5e1c41ebc2344dd Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 17:07:40 -0400 Subject: [PATCH 088/135] Add news fragment. --- newsfragments/4332.feature.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/4332.feature.rst diff --git a/newsfragments/4332.feature.rst b/newsfragments/4332.feature.rst new file mode 100644 index 0000000000..9f46298adc --- /dev/null +++ b/newsfragments/4332.feature.rst @@ -0,0 +1 @@ +Modernized and refactored VCS handling in package_index. \ No newline at end of file From 9bc2e87fc74e726927d208438385956591c96aa5 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 17:29:45 -0400 Subject: [PATCH 089/135] Ignore coverage for file urls. --- setuptools/package_index.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/package_index.py b/setuptools/package_index.py index bbc5846ed9..c3ffee41a7 100644 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -880,7 +880,7 @@ def _download_vcs(self, url, spec_filename): def _download_other(self, url, filename): scheme = urllib.parse.urlsplit(url).scheme - if scheme == 'file': + if scheme == 'file': # pragma: no cover return urllib.request.url2pathname(urllib.parse.urlparse(url).path) # raise error if not allowed self.url_ok(url, True) From 225d15808ba140799673b730e90b25e4117cc365 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 29 Apr 2024 20:19:04 -0400 Subject: [PATCH 090/135] Pin against pyproject-hooks==1.1 (docs). Closes #4333. --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 1226c940fc..0756fa92ea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -100,6 +100,9 @@ docs = sphinxcontrib-towncrier sphinx-notfound-page >=1,<2 + # workaround for pypa/setuptools#4333 + pyproject-hooks!=1.1 + ssl = certs = From a84b262a8d50ae8a3ed74bca58e6cadd1ac46495 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Tue, 30 Apr 2024 16:09:19 -0500 Subject: [PATCH 091/135] Typo fix in build_meta.py docstring --- setuptools/build_meta.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py index 2decd2d214..be2742d73d 100644 --- a/setuptools/build_meta.py +++ b/setuptools/build_meta.py @@ -2,7 +2,7 @@ Previously, when a user or a command line tool (let's call it a "frontend") needed to make a request of setuptools to take a certain action, for -example, generating a list of installation requirements, the frontend would +example, generating a list of installation requirements, the frontend would call "setup.py egg_info" or "setup.py bdist_wheel" on the command line. PEP 517 defines a different method of interfacing with setuptools. Rather From 9cf334d45e32d767d394fa6cc9ffa8829b150af0 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Wed, 1 May 2024 17:27:36 +0100 Subject: [PATCH 092/135] Avoid newer importlib-metadata APIs for backwards compatibility --- setuptools/dist.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 076f9a2327..03f6c0398b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -535,7 +535,8 @@ def warn_dash_deprecation(self, opt, section): def _setuptools_commands(self): try: - return metadata.distribution('setuptools').entry_points.names + entry_points = metadata.distribution('setuptools').entry_points + return {ep.name for ep in entry_points} # Avoid newer API for compatibility except metadata.PackageNotFoundError: # during bootstrapping, distribution doesn't exist return [] From 77bfebf36673858dc97c67155794adb6145a14fd Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 16:41:10 +0100 Subject: [PATCH 093/135] Re-enable deprecation checking in CI --- tox.ini | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tox.ini b/tox.ini index 22dd7af8da..c4cb4b5c64 100644 --- a/tox.ini +++ b/tox.ini @@ -4,8 +4,7 @@ deps = # Ideally all the dependencies should be set as "extras" setenv = PYTHONWARNDEFAULTENCODING = 1 - SETUPTOOLS_ENFORCE_DEPRECATION = {env:SETUPTOOLS_ENFORCE_DEPRECATION:0} - # ^-- Temporarily disable enforcement so CI don't fail on due dates + SETUPTOOLS_ENFORCE_DEPRECATION = {env:SETUPTOOLS_ENFORCE_DEPRECATION:1} commands = pytest {posargs} usedevelop = True From 118c5e44c1aca9ca67375a2e3a87988116986129 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 20:23:13 +0100 Subject: [PATCH 094/135] Remove deprecated 'setuptools.convert_path' --- setuptools/__init__.py | 17 ----------------- setuptools/tests/test_setuptools.py | 5 ----- 2 files changed, 22 deletions(-) diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 7c88c7e19b..a59bbe1177 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -8,7 +8,6 @@ import _distutils_hack.override # noqa: F401 import distutils.core from distutils.errors import DistutilsOptionError -from distutils.util import convert_path as _convert_path from . import logging, monkey from . import version as _version_module @@ -247,22 +246,6 @@ def findall(dir=os.curdir): return list(files) -@functools.wraps(_convert_path) -def convert_path(pathname): - SetuptoolsDeprecationWarning.emit( - "Access to implementation detail", - """ - The function `convert_path` is not provided by setuptools itself, - and therefore not part of the public API. - - Its direct usage by 3rd-party packages is considered improper and the function - may be removed in the future. - """, - due_date=(2023, 12, 13), # initial deprecation 2022-03-25, see #3201 - ) - return _convert_path(pathname) - - class sic(str): """Treat this string as-is (https://en.wikipedia.org/wiki/Sic)""" diff --git a/setuptools/tests/test_setuptools.py b/setuptools/tests/test_setuptools.py index 0dc4769b93..b1ca2396bd 100644 --- a/setuptools/tests/test_setuptools.py +++ b/setuptools/tests/test_setuptools.py @@ -307,8 +307,3 @@ def test_its_own_wheel_does_not_contain_tests(setuptools_wheel): for member in contents: assert '/tests/' not in member - - -def test_convert_path_deprecated(): - with pytest.warns(setuptools.SetuptoolsDeprecationWarning): - setuptools.convert_path('setuptools/tests') From 880b30ebba806c356686e8efc5fdc5fb4f910b81 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 20:24:39 +0100 Subject: [PATCH 095/135] Remove deprecated fallback for 'distutils.commands.build.build.sub_commands +=' --- setuptools/command/build.py | 18 ----------------- setuptools/tests/test_build.py | 36 +--------------------------------- 2 files changed, 1 insertion(+), 53 deletions(-) diff --git a/setuptools/command/build.py b/setuptools/command/build.py index afda7e3be9..16c077b7cc 100644 --- a/setuptools/command/build.py +++ b/setuptools/command/build.py @@ -1,8 +1,6 @@ from typing import Dict, List, Protocol from distutils.command.build import build as _build -from ..warnings import SetuptoolsDeprecationWarning - _ORIGINAL_SUBCOMMANDS = {"build_py", "build_clib", "build_ext", "build_scripts"} @@ -10,22 +8,6 @@ class build(_build): # copy to avoid sharing the object with parent class sub_commands = _build.sub_commands[:] - def get_sub_commands(self): - subcommands = {cmd[0] for cmd in _build.sub_commands} - if subcommands - _ORIGINAL_SUBCOMMANDS: - SetuptoolsDeprecationWarning.emit( - "Direct usage of `distutils` commands", - """ - It seems that you are using `distutils.command.build` to add - new subcommands. Using `distutils` directly is considered deprecated, - please use `setuptools.command.build`. - """, - due_date=(2023, 12, 13), # Warning introduced in 13 Jun 2022. - see_url="https://peps.python.org/pep-0632/", - ) - self.sub_commands = _build.sub_commands - return super().get_sub_commands() - class SubCommand(Protocol): """In order to support editable installations (see :pep:`660`) all diff --git a/setuptools/tests/test_build.py b/setuptools/tests/test_build.py index 4a3b11de18..141522efd4 100644 --- a/setuptools/tests/test_build.py +++ b/setuptools/tests/test_build.py @@ -1,10 +1,6 @@ -from contextlib import contextmanager -from setuptools import Command, SetuptoolsDeprecationWarning +from setuptools import Command from setuptools.dist import Distribution from setuptools.command.build import build -from distutils.command.build import build as distutils_build - -import pytest def test_distribution_gives_setuptools_build_obj(tmpdir_cwd): @@ -24,15 +20,6 @@ def test_distribution_gives_setuptools_build_obj(tmpdir_cwd): assert isinstance(dist.get_command_obj("build"), build) -@contextmanager -def _restore_sub_commands(): - orig = distutils_build.sub_commands[:] - try: - yield - finally: - distutils_build.sub_commands = orig - - class Subcommand(Command): """Dummy command to be used in tests""" @@ -44,24 +31,3 @@ def finalize_options(self): def run(self): raise NotImplementedError("just to check if the command runs") - - -@_restore_sub_commands() -def test_subcommand_in_distutils(tmpdir_cwd): - """ - Ensure that sub commands registered in ``distutils`` run, - after instructing the users to migrate to ``setuptools``. - """ - dist = Distribution( - dict( - packages=[], - cmdclass={'subcommand': Subcommand}, - ) - ) - distutils_build.sub_commands.append(('subcommand', None)) - - warning_msg = "please use .setuptools.command.build." - with pytest.warns(SetuptoolsDeprecationWarning, match=warning_msg): - # For backward compatibility, the subcommand should run anyway: - with pytest.raises(NotImplementedError, match="the command runs"): - dist.run_command("build") From a32e2aa10cb56ef2338b6b6289140ec6525caaff Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Mon, 22 Apr 2024 23:10:14 +0100 Subject: [PATCH 096/135] Add news fragments --- newsfragments/4322.removal.1.rst | 3 +++ newsfragments/4322.removal.2.rst | 3 +++ 2 files changed, 6 insertions(+) create mode 100644 newsfragments/4322.removal.1.rst create mode 100644 newsfragments/4322.removal.2.rst diff --git a/newsfragments/4322.removal.1.rst b/newsfragments/4322.removal.1.rst new file mode 100644 index 0000000000..33360172d5 --- /dev/null +++ b/newsfragments/4322.removal.1.rst @@ -0,0 +1,3 @@ +Remove ``setuptools.convert_path`` after long deprecation period. +This function was never defined by ``setuptools`` itself, but rather a +side-effect of an import for internal usage. diff --git a/newsfragments/4322.removal.2.rst b/newsfragments/4322.removal.2.rst new file mode 100644 index 0000000000..88380f4c8d --- /dev/null +++ b/newsfragments/4322.removal.2.rst @@ -0,0 +1,3 @@ +Remove fallback for customisations of ``distutils``' ``build.sub_command`` after long +deprecated period. +Users are advised to import ``build`` directly from ``setuptools.command.build``. From 94f0089a9f9e0f4ab38840fe9a03c1335089e4a5 Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 5 Mar 2024 20:30:58 -0500 Subject: [PATCH 097/135] Type _declare_state and make it work statically --- pkg_resources/__init__.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index d32b095a88..7693b071e7 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -27,7 +27,7 @@ import time import re import types -from typing import List, Protocol +from typing import Any, Dict, List, Protocol import zipfile import zipimport import warnings @@ -94,9 +94,6 @@ resource_listdir = None resource_filename = None resource_exists = None -_distribution_finders = None -_namespace_handlers = None -_namespace_packages = None warnings.warn( @@ -120,11 +117,10 @@ class PEP440Warning(RuntimeWarning): parse_version = packaging.version.Version -_state_vars = {} +_state_vars: Dict[str, Any] = {} -def _declare_state(vartype, **kw): - globals().update(kw) +def _declare_state(vartype: str, **kw: object) -> None: _state_vars.update(dict.fromkeys(kw, vartype)) @@ -2025,7 +2021,8 @@ def __init__(self, importer): self._setup_prefix() -_declare_state('dict', _distribution_finders={}) +_distribution_finders = {} +_declare_state('dict', _distribution_finders=_distribution_finders) def register_finder(importer_type, distribution_finder): @@ -2198,8 +2195,10 @@ def resolve_egg_link(path): register_finder(importlib.machinery.FileFinder, find_on_path) -_declare_state('dict', _namespace_handlers={}) -_declare_state('dict', _namespace_packages={}) +_namespace_handlers = {} +_declare_state('dict', _namespace_handlers=_namespace_handlers) +_namespace_packages = {} +_declare_state('dict', _namespace_packages=_namespace_packages) def register_namespace_handler(importer_type, namespace_handler): From 957e991abd3d5a5c6eeafb0545a71ab30f53a917 Mon Sep 17 00:00:00 2001 From: Avasam Date: Wed, 6 Mar 2024 12:19:38 -0500 Subject: [PATCH 098/135] Add type annotations to moved definitions --- pkg_resources/__init__.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 7693b071e7..d3213b3237 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -27,7 +27,7 @@ import time import re import types -from typing import Any, Dict, List, Protocol +from typing import Any, Callable, Dict, Iterable, List, Protocol, Optional import zipfile import zipimport import warnings @@ -2021,7 +2021,9 @@ def __init__(self, importer): self._setup_prefix() -_distribution_finders = {} +_distribution_finders: Dict[ + type, Callable[[object, str, bool], Iterable["Distribution"]] +] = {} _declare_state('dict', _distribution_finders=_distribution_finders) @@ -2195,9 +2197,11 @@ def resolve_egg_link(path): register_finder(importlib.machinery.FileFinder, find_on_path) -_namespace_handlers = {} +_namespace_handlers: Dict[ + type, Callable[[object, str, str, types.ModuleType], Optional[str]] +] = {} _declare_state('dict', _namespace_handlers=_namespace_handlers) -_namespace_packages = {} +_namespace_packages: Dict[Optional[str], List[str]] = {} _declare_state('dict', _namespace_packages=_namespace_packages) From 7f59cd1bd026133e3705b7837401c0df13a5d947 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 7 May 2024 18:49:38 +0100 Subject: [PATCH 099/135] Update used version validate-pyproject to 0.17 --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c4cb4b5c64..e13ad53da3 100644 --- a/tox.ini +++ b/tox.ini @@ -82,7 +82,7 @@ commands = [testenv:generate-validation-code] skip_install = True deps = - validate-pyproject[all]==0.12.2 + validate-pyproject[all]==0.17 commands = python -m tools.generate_validation_code From 8283cb66244bc90f3510cbe6236f84e92a6876f9 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Tue, 7 May 2024 18:56:45 +0100 Subject: [PATCH 100/135] Update code automatically generated by validate_pyproject --- setuptools/config/_validate_pyproject/NOTICE | 1 - .../config/_validate_pyproject/__init__.py | 2 +- .../_validate_pyproject/error_reporting.py | 46 ++- .../_validate_pyproject/extra_validations.py | 28 +- .../fastjsonschema_validations.py | 389 ++++++++++-------- .../config/_validate_pyproject/formats.py | 91 +++- 6 files changed, 362 insertions(+), 195 deletions(-) diff --git a/setuptools/config/_validate_pyproject/NOTICE b/setuptools/config/_validate_pyproject/NOTICE index 286d29082e..121ba5fd22 100644 --- a/setuptools/config/_validate_pyproject/NOTICE +++ b/setuptools/config/_validate_pyproject/NOTICE @@ -436,4 +436,3 @@ Exhibit B - "Incompatible With Secondary Licenses" Notice This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. - diff --git a/setuptools/config/_validate_pyproject/__init__.py b/setuptools/config/_validate_pyproject/__init__.py index dbe6cb4ca4..4f612bd51c 100644 --- a/setuptools/config/_validate_pyproject/__init__.py +++ b/setuptools/config/_validate_pyproject/__init__.py @@ -30,5 +30,5 @@ def validate(data: Any) -> bool: """ with detailed_errors(): _validate(data, custom_formats=FORMAT_FUNCTIONS) - reduce(lambda acc, fn: fn(acc), EXTRA_VALIDATIONS, data) + reduce(lambda acc, fn: fn(acc), EXTRA_VALIDATIONS, data) return True diff --git a/setuptools/config/_validate_pyproject/error_reporting.py b/setuptools/config/_validate_pyproject/error_reporting.py index d44e290e36..a6753604f5 100644 --- a/setuptools/config/_validate_pyproject/error_reporting.py +++ b/setuptools/config/_validate_pyproject/error_reporting.py @@ -3,12 +3,21 @@ import logging import os import re +import typing from contextlib import contextmanager from textwrap import indent, wrap -from typing import Any, Dict, Iterator, List, Optional, Sequence, Union, cast +from typing import Any, Dict, Generator, Iterator, List, Optional, Sequence, Union, cast from .fastjsonschema_exceptions import JsonSchemaValueException +if typing.TYPE_CHECKING: + import sys + + if sys.version_info < (3, 11): + from typing_extensions import Self + else: + from typing import Self + _logger = logging.getLogger(__name__) _MESSAGE_REPLACEMENTS = { @@ -36,6 +45,11 @@ "property names": "keys", } +_FORMATS_HELP = """ +For more details about `format` see +https://validate-pyproject.readthedocs.io/en/latest/api/validate_pyproject.formats.html +""" + class ValidationError(JsonSchemaValueException): """Report violations of a given JSON schema. @@ -59,7 +73,7 @@ class ValidationError(JsonSchemaValueException): _original_message = "" @classmethod - def _from_jsonschema(cls, ex: JsonSchemaValueException): + def _from_jsonschema(cls, ex: JsonSchemaValueException) -> "Self": formatter = _ErrorFormatting(ex) obj = cls(str(formatter), ex.value, formatter.name, ex.definition, ex.rule) debug_code = os.getenv("JSONSCHEMA_DEBUG_CODE_GENERATION", "false").lower() @@ -72,7 +86,7 @@ def _from_jsonschema(cls, ex: JsonSchemaValueException): @contextmanager -def detailed_errors(): +def detailed_errors() -> Generator[None, None, None]: try: yield except JsonSchemaValueException as ex: @@ -83,7 +97,7 @@ class _ErrorFormatting: def __init__(self, ex: JsonSchemaValueException): self.ex = ex self.name = f"`{self._simplify_name(ex.name)}`" - self._original_message = self.ex.message.replace(ex.name, self.name) + self._original_message: str = self.ex.message.replace(ex.name, self.name) self._summary = "" self._details = "" @@ -107,11 +121,12 @@ def details(self) -> str: return self._details - def _simplify_name(self, name): + @staticmethod + def _simplify_name(name: str) -> str: x = len("data.") return name[x:] if name.startswith("data.") else name - def _expand_summary(self): + def _expand_summary(self) -> str: msg = self._original_message for bad, repl in _MESSAGE_REPLACEMENTS.items(): @@ -129,8 +144,9 @@ def _expand_summary(self): def _expand_details(self) -> str: optional = [] - desc_lines = self.ex.definition.pop("$$description", []) - desc = self.ex.definition.pop("description", None) or " ".join(desc_lines) + definition = self.ex.definition or {} + desc_lines = definition.pop("$$description", []) + desc = definition.pop("description", None) or " ".join(desc_lines) if desc: description = "\n".join( wrap( @@ -142,18 +158,20 @@ def _expand_details(self) -> str: ) ) optional.append(f"DESCRIPTION:\n{description}") - schema = json.dumps(self.ex.definition, indent=4) + schema = json.dumps(definition, indent=4) value = json.dumps(self.ex.value, indent=4) defaults = [ f"GIVEN VALUE:\n{indent(value, ' ')}", f"OFFENDING RULE: {self.ex.rule!r}", f"DEFINITION:\n{indent(schema, ' ')}", ] - return "\n\n".join(optional + defaults) + msg = "\n\n".join(optional + defaults) + epilog = f"\n{_FORMATS_HELP}" if "format" in msg.lower() else "" + return msg + epilog class _SummaryWriter: - _IGNORE = {"description", "default", "title", "examples"} + _IGNORE = frozenset(("description", "default", "title", "examples")) def __init__(self, jargon: Optional[Dict[str, str]] = None): self.jargon: Dict[str, str] = jargon or {} @@ -242,7 +260,9 @@ def _is_unecessary(self, path: Sequence[str]) -> bool: key = path[-1] return any(key.startswith(k) for k in "$_") or key in self._IGNORE - def _filter_unecessary(self, schema: dict, path: Sequence[str]): + def _filter_unecessary( + self, schema: Dict[str, Any], path: Sequence[str] + ) -> Dict[str, Any]: return { key: value for key, value in schema.items() @@ -271,7 +291,7 @@ def _handle_list( self(v, item_prefix, _path=[*path, f"[{i}]"]) for i, v in enumerate(schemas) ) - def _is_property(self, path: Sequence[str]): + def _is_property(self, path: Sequence[str]) -> bool: """Check if the given path can correspond to an arbitrarily named property""" counter = 0 for key in path[-2::-1]: diff --git a/setuptools/config/_validate_pyproject/extra_validations.py b/setuptools/config/_validate_pyproject/extra_validations.py index 4130a421cf..c4ffe651dd 100644 --- a/setuptools/config/_validate_pyproject/extra_validations.py +++ b/setuptools/config/_validate_pyproject/extra_validations.py @@ -3,6 +3,7 @@ JSON Schema library). """ +from inspect import cleandoc from typing import Mapping, TypeVar from .error_reporting import ValidationError @@ -11,11 +12,16 @@ class RedefiningStaticFieldAsDynamic(ValidationError): - """According to PEP 621: + _DESC = """According to PEP 621: Build back-ends MUST raise an error if the metadata specifies a field statically as well as being listed in dynamic. """ + __doc__ = _DESC + _URL = ( + "https://packaging.python.org/en/latest/specifications/" + "pyproject-toml/#dynamic" + ) def validate_project_dynamic(pyproject: T) -> T: @@ -24,11 +30,21 @@ def validate_project_dynamic(pyproject: T) -> T: for field in dynamic: if field in project_table: - msg = f"You cannot provide a value for `project.{field}` and " - msg += "list it under `project.dynamic` at the same time" - name = f"data.project.{field}" - value = {field: project_table[field], "...": " # ...", "dynamic": dynamic} - raise RedefiningStaticFieldAsDynamic(msg, value, name, rule="PEP 621") + raise RedefiningStaticFieldAsDynamic( + message=f"You cannot provide a value for `project.{field}` and " + "list it under `project.dynamic` at the same time", + value={ + field: project_table[field], + "...": " # ...", + "dynamic": dynamic, + }, + name=f"data.project.{field}", + definition={ + "description": cleandoc(RedefiningStaticFieldAsDynamic._DESC), + "see": RedefiningStaticFieldAsDynamic._URL, + }, + rule="PEP 621", + ) return pyproject diff --git a/setuptools/config/_validate_pyproject/fastjsonschema_validations.py b/setuptools/config/_validate_pyproject/fastjsonschema_validations.py index 8b852bbfd4..1c58a55ea8 100644 --- a/setuptools/config/_validate_pyproject/fastjsonschema_validations.py +++ b/setuptools/config/_validate_pyproject/fastjsonschema_validations.py @@ -1,4 +1,5 @@ # noqa +# ruff: noqa # flake8: noqa # pylint: skip-file # mypy: ignore-errors @@ -9,7 +10,8 @@ # *** PLEASE DO NOT MODIFY DIRECTLY: Automatically generated code *** -VERSION = "2.16.3" +VERSION = "2.19.1" +from decimal import Decimal import re from .fastjsonschema_exceptions import JsonSchemaValueException @@ -29,7 +31,7 @@ def validate(data, custom_formats={}, name_prefix=None): def validate_https___packaging_python_org_en_latest_specifications_declaring_build_dependencies(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -40,9 +42,9 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_bui raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must be object", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='type') data__buildsystem_is_dict = isinstance(data__buildsystem, dict) if data__buildsystem_is_dict: - data__buildsystem_len = len(data__buildsystem) - if not all(prop in data__buildsystem for prop in ['requires']): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must contain ['requires'] properties", value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='required') + data__buildsystem__missing_keys = set(['requires']) - data__buildsystem.keys() + if data__buildsystem__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".build-system must contain " + (str(sorted(data__buildsystem__missing_keys)) + " properties"), value=data__buildsystem, name="" + (name_prefix or "data") + ".build-system", definition={'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, rule='required') data__buildsystem_keys = set(data__buildsystem.keys()) if "requires" in data__buildsystem_keys: data__buildsystem_keys.remove("requires") @@ -79,30 +81,30 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_bui if "project" in data_keys: data_keys.remove("project") data__project = data["project"] - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata(data__project, custom_formats, (name_prefix or "data") + ".project") + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml(data__project, custom_formats, (name_prefix or "data") + ".project") if "tool" in data_keys: data_keys.remove("tool") data__tool = data["tool"] if not isinstance(data__tool, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".tool must be object", value=data__tool, name="" + (name_prefix or "data") + ".tool", definition={'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}, rule='type') data__tool_is_dict = isinstance(data__tool, dict) if data__tool_is_dict: data__tool_keys = set(data__tool.keys()) if "distutils" in data__tool_keys: data__tool_keys.remove("distutils") data__tool__distutils = data__tool["distutils"] - validate_https___docs_python_org_3_install(data__tool__distutils, custom_formats, (name_prefix or "data") + ".tool.distutils") + validate_https___setuptools_pypa_io_en_latest_deprecated_distutils_configfile_html(data__tool__distutils, custom_formats, (name_prefix or "data") + ".tool.distutils") if "setuptools" in data__tool_keys: data__tool_keys.remove("setuptools") data__tool__setuptools = data__tool["setuptools"] - validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data__tool__setuptools, custom_formats, (name_prefix or "data") + ".tool.setuptools") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html(data__tool__setuptools, custom_formats, (name_prefix or "data") + ".tool.setuptools") if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive'}}}, 'readme': {'anyOf': [{'$ref': '#/definitions/file-directive'}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-build-dependencies/', 'title': 'Data structure for ``pyproject.toml`` files', '$$description': ['File format containing build-time configurations for the Python ecosystem. ', ':pep:`517` initially defined a build-system independent format for source trees', 'which was complemented by :pep:`518` to provide a way of specifying dependencies ', 'for building Python projects.', 'Please notice the ``project`` table (as initially defined in :pep:`621`) is not included', 'in this schema and should be considered separately.'], 'type': 'object', 'additionalProperties': False, 'properties': {'build-system': {'type': 'object', 'description': 'Table used to store build-related data', 'additionalProperties': False, 'properties': {'requires': {'type': 'array', '$$description': ['List of dependencies in the :pep:`508` format required to execute the build', 'system. Please notice that the resulting dependency graph', '**MUST NOT contain cycles**'], 'items': {'type': 'string'}}, 'build-backend': {'type': 'string', 'description': 'Python object that will be used to perform the build according to :pep:`517`', 'format': 'pep517-backend-reference'}, 'backend-path': {'type': 'array', '$$description': ['List of directories to be prepended to ``sys.path`` when loading the', 'back-end, and running its hooks'], 'items': {'type': 'string', '$comment': 'Should be a path (TODO: enforce it with format?)'}}}, 'required': ['requires']}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, 'tool': {'type': 'object', 'properties': {'distutils': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, 'setuptools': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$ref': '#/definitions/package-name'}}, {'$ref': '#/definitions/find-directive'}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$ref': '#/definitions/package-name'}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'$ref': '#/definitions/attr-directive'}, {'$ref': '#/definitions/file-directive'}]}, 'classifiers': {'$ref': '#/definitions/file-directive'}, 'description': {'$ref': '#/definitions/file-directive'}, 'entry-points': {'$ref': '#/definitions/file-directive'}, 'dependencies': {'$ref': '#/definitions/file-directive-for-dependencies'}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$ref': '#/definitions/file-directive-for-dependencies'}}}, 'readme': {'type': 'object', 'anyOf': [{'$ref': '#/definitions/file-directive'}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'$ref': '#/definitions/file-directive/properties/file'}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}}}}, 'project': {'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$ref': '#/definitions/author'}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create command-line wrappers for the given', '`entry points `_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points `_.', 'The difference between ``scripts`` and ``gui-scripts`` is only relevant in', 'Windows.']}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$ref': '#/definitions/entry-point-group'}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$ref': '#/definitions/dependency'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$ref': '#/definitions/dependency'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') return data -def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, custom_formats={}, name_prefix=None): +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -149,12 +151,12 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data_keys.remove("zip-safe") data__zipsafe = data["zip-safe"] if not isinstance(data__zipsafe, (bool)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".zip-safe must be boolean", value=data__zipsafe, name="" + (name_prefix or "data") + ".zip-safe", definition={'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".zip-safe must be boolean", value=data__zipsafe, name="" + (name_prefix or "data") + ".zip-safe", definition={'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, rule='type') if "script-files" in data_keys: data_keys.remove("script-files") data__scriptfiles = data["script-files"] if not isinstance(data__scriptfiles, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".script-files must be array", value=data__scriptfiles, name="" + (name_prefix or "data") + ".script-files", definition={'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".script-files must be array", value=data__scriptfiles, name="" + (name_prefix or "data") + ".script-files", definition={'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, rule='type') data__scriptfiles_is_list = isinstance(data__scriptfiles, (list, tuple)) if data__scriptfiles_is_list: data__scriptfiles_len = len(data__scriptfiles) @@ -165,7 +167,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data_keys.remove("eager-resources") data__eagerresources = data["eager-resources"] if not isinstance(data__eagerresources, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".eager-resources must be array", value=data__eagerresources, name="" + (name_prefix or "data") + ".eager-resources", definition={'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".eager-resources must be array", value=data__eagerresources, name="" + (name_prefix or "data") + ".eager-resources", definition={'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, rule='type') data__eagerresources_is_list = isinstance(data__eagerresources, (list, tuple)) if data__eagerresources_is_list: data__eagerresources_len = len(data__eagerresources) @@ -179,26 +181,26 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if data__packages_one_of_count1 < 2: try: if not isinstance(data__packages, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be array", value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, rule='type') data__packages_is_list = isinstance(data__packages, (list, tuple)) if data__packages_is_list: data__packages_len = len(data__packages) for data__packages_x, data__packages_item in enumerate(data__packages): - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data__packages_item, custom_formats, (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals())) + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data__packages_item, custom_formats, (name_prefix or "data") + ".packages[{data__packages_x}]".format(**locals())) data__packages_one_of_count1 += 1 except JsonSchemaValueException: pass if data__packages_one_of_count1 < 2: try: - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_find_directive(data__packages, custom_formats, (name_prefix or "data") + ".packages") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_find_directive(data__packages, custom_formats, (name_prefix or "data") + ".packages") data__packages_one_of_count1 += 1 except JsonSchemaValueException: pass if data__packages_one_of_count1 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count1) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count1) + " matches found)"), value=data__packages, name="" + (name_prefix or "data") + ".packages", definition={'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, rule='oneOf') if "package-dir" in data_keys: data_keys.remove("package-dir") data__packagedir = data["package-dir"] if not isinstance(data__packagedir, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be object", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='type') data__packagedir_is_dict = isinstance(data__packagedir, dict) if data__packagedir_is_dict: data__packagedir_keys = set(data__packagedir.keys()) @@ -209,35 +211,35 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if not isinstance(data__packagedir_val, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + " must be string", value=data__packagedir_val, name="" + (name_prefix or "data") + ".package-dir.{data__packagedir_key}".format(**locals()) + "", definition={'type': 'string'}, rule='type') if data__packagedir_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must not contain "+str(data__packagedir_keys)+" properties", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='additionalProperties') data__packagedir_len = len(data__packagedir) if data__packagedir_len != 0: data__packagedir_property_names = True for data__packagedir_key in data__packagedir: try: - data__packagedir_key_one_of_count2 = 0 - if data__packagedir_key_one_of_count2 < 2: + data__packagedir_key_any_of_count2 = 0 + if not data__packagedir_key_any_of_count2: try: if data__packagedir_key != "": raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be same as const definition: ", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'const': ''}, rule='const') - data__packagedir_key_one_of_count2 += 1 + data__packagedir_key_any_of_count2 += 1 except JsonSchemaValueException: pass - if data__packagedir_key_one_of_count2 < 2: + if not data__packagedir_key_any_of_count2: try: - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data__packagedir_key, custom_formats, (name_prefix or "data") + ".package-dir") - data__packagedir_key_one_of_count2 += 1 + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data__packagedir_key, custom_formats, (name_prefix or "data") + ".package-dir") + data__packagedir_key_any_of_count2 += 1 except JsonSchemaValueException: pass - if data__packagedir_key_one_of_count2 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be valid exactly by one definition" + (" (" + str(data__packagedir_key_one_of_count2) + " matches found)"), value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, rule='oneOf') + if not data__packagedir_key_any_of_count2: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir cannot be validated by any definition", value=data__packagedir_key, name="" + (name_prefix or "data") + ".package-dir", definition={'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, rule='anyOf') except JsonSchemaValueException: data__packagedir_property_names = False if not data__packagedir_property_names: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-dir must be named by propertyName definition", value=data__packagedir, name="" + (name_prefix or "data") + ".package-dir", definition={'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, rule='propertyNames') if "package-data" in data_keys: data_keys.remove("package-data") data__packagedata = data["package-data"] if not isinstance(data__packagedata, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be object", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be object", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') data__packagedata_is_dict = isinstance(data__packagedata, dict) if data__packagedata_is_dict: data__packagedata_keys = set(data__packagedata.keys()) @@ -254,32 +256,34 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if not isinstance(data__packagedata_val_item, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data.{data__packagedata_key}[{data__packagedata_val_x}]".format(**locals()) + " must be string", value=data__packagedata_val_item, name="" + (name_prefix or "data") + ".package-data.{data__packagedata_key}[{data__packagedata_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') if data__packagedata_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must not contain "+str(data__packagedata_keys)+" properties", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must not contain "+str(data__packagedata_keys)+" properties", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties') data__packagedata_len = len(data__packagedata) if data__packagedata_len != 0: data__packagedata_property_names = True for data__packagedata_key in data__packagedata: try: - data__packagedata_key_one_of_count3 = 0 - if data__packagedata_key_one_of_count3 < 2: + data__packagedata_key_any_of_count3 = 0 + if not data__packagedata_key_any_of_count3: try: + if not isinstance(data__packagedata_key, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be string", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') if isinstance(data__packagedata_key, str): if not custom_formats["python-module-name"](data__packagedata_key): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be python-module-name", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'format': 'python-module-name'}, rule='format') - data__packagedata_key_one_of_count3 += 1 + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be python-module-name", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + data__packagedata_key_any_of_count3 += 1 except JsonSchemaValueException: pass - if data__packagedata_key_one_of_count3 < 2: + if not data__packagedata_key_any_of_count3: try: if data__packagedata_key != "*": raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be same as const definition: *", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'const': '*'}, rule='const') - data__packagedata_key_one_of_count3 += 1 + data__packagedata_key_any_of_count3 += 1 except JsonSchemaValueException: pass - if data__packagedata_key_one_of_count3 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be valid exactly by one definition" + (" (" + str(data__packagedata_key_one_of_count3) + " matches found)"), value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, rule='oneOf') + if not data__packagedata_key_any_of_count3: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data cannot be validated by any definition", value=data__packagedata_key, name="" + (name_prefix or "data") + ".package-data", definition={'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, rule='anyOf') except JsonSchemaValueException: data__packagedata_property_names = False if not data__packagedata_property_names: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be named by propertyName definition", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".package-data must be named by propertyName definition", value=data__packagedata, name="" + (name_prefix or "data") + ".package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames') if "include-package-data" in data_keys: data_keys.remove("include-package-data") data__includepackagedata = data["include-package-data"] @@ -289,7 +293,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data_keys.remove("exclude-package-data") data__excludepackagedata = data["exclude-package-data"] if not isinstance(data__excludepackagedata, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be object", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be object", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') data__excludepackagedata_is_dict = isinstance(data__excludepackagedata, dict) if data__excludepackagedata_is_dict: data__excludepackagedata_keys = set(data__excludepackagedata.keys()) @@ -306,37 +310,39 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if not isinstance(data__excludepackagedata_val_item, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}[{data__excludepackagedata_val_x}]".format(**locals()) + " must be string", value=data__excludepackagedata_val_item, name="" + (name_prefix or "data") + ".exclude-package-data.{data__excludepackagedata_key}[{data__excludepackagedata_val_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') if data__excludepackagedata_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must not contain "+str(data__excludepackagedata_keys)+" properties", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must not contain "+str(data__excludepackagedata_keys)+" properties", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='additionalProperties') data__excludepackagedata_len = len(data__excludepackagedata) if data__excludepackagedata_len != 0: data__excludepackagedata_property_names = True for data__excludepackagedata_key in data__excludepackagedata: try: - data__excludepackagedata_key_one_of_count4 = 0 - if data__excludepackagedata_key_one_of_count4 < 2: + data__excludepackagedata_key_any_of_count4 = 0 + if not data__excludepackagedata_key_any_of_count4: try: + if not isinstance(data__excludepackagedata_key, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be string", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') if isinstance(data__excludepackagedata_key, str): if not custom_formats["python-module-name"](data__excludepackagedata_key): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be python-module-name", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'format': 'python-module-name'}, rule='format') - data__excludepackagedata_key_one_of_count4 += 1 + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be python-module-name", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + data__excludepackagedata_key_any_of_count4 += 1 except JsonSchemaValueException: pass - if data__excludepackagedata_key_one_of_count4 < 2: + if not data__excludepackagedata_key_any_of_count4: try: if data__excludepackagedata_key != "*": raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be same as const definition: *", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'const': '*'}, rule='const') - data__excludepackagedata_key_one_of_count4 += 1 + data__excludepackagedata_key_any_of_count4 += 1 except JsonSchemaValueException: pass - if data__excludepackagedata_key_one_of_count4 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be valid exactly by one definition" + (" (" + str(data__excludepackagedata_key_one_of_count4) + " matches found)"), value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, rule='oneOf') + if not data__excludepackagedata_key_any_of_count4: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data cannot be validated by any definition", value=data__excludepackagedata_key, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, rule='anyOf') except JsonSchemaValueException: data__excludepackagedata_property_names = False if not data__excludepackagedata_property_names: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be named by propertyName definition", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".exclude-package-data must be named by propertyName definition", value=data__excludepackagedata, name="" + (name_prefix or "data") + ".exclude-package-data", definition={'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='propertyNames') if "namespace-packages" in data_keys: data_keys.remove("namespace-packages") data__namespacepackages = data["namespace-packages"] if not isinstance(data__namespacepackages, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages must be array", value=data__namespacepackages, name="" + (name_prefix or "data") + ".namespace-packages", definition={'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".namespace-packages must be array", value=data__namespacepackages, name="" + (name_prefix or "data") + ".namespace-packages", definition={'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, rule='type') data__namespacepackages_is_list = isinstance(data__namespacepackages, (list, tuple)) if data__namespacepackages_is_list: data__namespacepackages_len = len(data__namespacepackages) @@ -364,7 +370,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data_keys.remove("data-files") data__datafiles = data["data-files"] if not isinstance(data__datafiles, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files must be object", value=data__datafiles, name="" + (name_prefix or "data") + ".data-files", definition={'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".data-files must be object", value=data__datafiles, name="" + (name_prefix or "data") + ".data-files", definition={'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, rule='type') data__datafiles_is_dict = isinstance(data__datafiles, dict) if data__datafiles_is_dict: data__datafiles_keys = set(data__datafiles.keys()) @@ -401,7 +407,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data_keys.remove("license-files") data__licensefiles = data["license-files"] if not isinstance(data__licensefiles, (list, tuple)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, rule='type') data__licensefiles_is_list = isinstance(data__licensefiles, (list, tuple)) if data__licensefiles_is_list: data__licensefiles_len = len(data__licensefiles) @@ -412,7 +418,7 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data_keys.remove("dynamic") data__dynamic = data["dynamic"] if not isinstance(data__dynamic, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be object", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be object", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}, rule='type') data__dynamic_is_dict = isinstance(data__dynamic, dict) if data__dynamic_is_dict: data__dynamic_keys = set(data__dynamic.keys()) @@ -422,37 +428,37 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data__dynamic__version_one_of_count5 = 0 if data__dynamic__version_one_of_count5 < 2: try: - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_attr_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_attr_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version") data__dynamic__version_one_of_count5 += 1 except JsonSchemaValueException: pass if data__dynamic__version_one_of_count5 < 2: try: - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__version, custom_formats, (name_prefix or "data") + ".dynamic.version") data__dynamic__version_one_of_count5 += 1 except JsonSchemaValueException: pass if data__dynamic__version_one_of_count5 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.version must be valid exactly by one definition" + (" (" + str(data__dynamic__version_one_of_count5) + " matches found)"), value=data__dynamic__version, name="" + (name_prefix or "data") + ".dynamic.version", definition={'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, rule='oneOf') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.version must be valid exactly by one definition" + (" (" + str(data__dynamic__version_one_of_count5) + " matches found)"), value=data__dynamic__version, name="" + (name_prefix or "data") + ".dynamic.version", definition={'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, rule='oneOf') if "classifiers" in data__dynamic_keys: data__dynamic_keys.remove("classifiers") data__dynamic__classifiers = data__dynamic["classifiers"] - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__classifiers, custom_formats, (name_prefix or "data") + ".dynamic.classifiers") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__classifiers, custom_formats, (name_prefix or "data") + ".dynamic.classifiers") if "description" in data__dynamic_keys: data__dynamic_keys.remove("description") data__dynamic__description = data__dynamic["description"] - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__description, custom_formats, (name_prefix or "data") + ".dynamic.description") - if "dependencies" in data__dynamic_keys: - data__dynamic_keys.remove("dependencies") - data__dynamic__dependencies = data__dynamic["dependencies"] - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__dependencies, custom_formats, (name_prefix or "data") + ".dynamic.dependencies") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__description, custom_formats, (name_prefix or "data") + ".dynamic.description") if "entry-points" in data__dynamic_keys: data__dynamic_keys.remove("entry-points") data__dynamic__entrypoints = data__dynamic["entry-points"] - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__entrypoints, custom_formats, (name_prefix or "data") + ".dynamic.entry-points") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__entrypoints, custom_formats, (name_prefix or "data") + ".dynamic.entry-points") + if "dependencies" in data__dynamic_keys: + data__dynamic_keys.remove("dependencies") + data__dynamic__dependencies = data__dynamic["dependencies"] + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_for_dependencies(data__dynamic__dependencies, custom_formats, (name_prefix or "data") + ".dynamic.dependencies") if "optional-dependencies" in data__dynamic_keys: data__dynamic_keys.remove("optional-dependencies") data__dynamic__optionaldependencies = data__dynamic["optional-dependencies"] if not isinstance(data__dynamic__optionaldependencies, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be object", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be object", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, rule='type') data__dynamic__optionaldependencies_is_dict = isinstance(data__dynamic__optionaldependencies, dict) if data__dynamic__optionaldependencies_is_dict: data__dynamic__optionaldependencies_keys = set(data__dynamic__optionaldependencies.keys()) @@ -460,32 +466,38 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, if REGEX_PATTERNS['.+'].search(data__dynamic__optionaldependencies_key): if data__dynamic__optionaldependencies_key in data__dynamic__optionaldependencies_keys: data__dynamic__optionaldependencies_keys.remove(data__dynamic__optionaldependencies_key) - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__optionaldependencies_val, custom_formats, (name_prefix or "data") + ".dynamic.optional-dependencies.{data__dynamic__optionaldependencies_key}".format(**locals())) + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_for_dependencies(data__dynamic__optionaldependencies_val, custom_formats, (name_prefix or "data") + ".dynamic.optional-dependencies.{data__dynamic__optionaldependencies_key}".format(**locals())) if data__dynamic__optionaldependencies_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must not contain "+str(data__dynamic__optionaldependencies_keys)+" properties", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must not contain "+str(data__dynamic__optionaldependencies_keys)+" properties", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, rule='additionalProperties') data__dynamic__optionaldependencies_len = len(data__dynamic__optionaldependencies) if data__dynamic__optionaldependencies_len != 0: data__dynamic__optionaldependencies_property_names = True for data__dynamic__optionaldependencies_key in data__dynamic__optionaldependencies: try: + if not isinstance(data__dynamic__optionaldependencies_key, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be string", value=data__dynamic__optionaldependencies_key, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='type') if isinstance(data__dynamic__optionaldependencies_key, str): - if not custom_formats["python-identifier"](data__dynamic__optionaldependencies_key): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be python-identifier", value=data__dynamic__optionaldependencies_key, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'format': 'python-identifier'}, rule='format') + if not custom_formats["pep508-identifier"](data__dynamic__optionaldependencies_key): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be pep508-identifier", value=data__dynamic__optionaldependencies_key, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'string', 'format': 'pep508-identifier'}, rule='format') except JsonSchemaValueException: data__dynamic__optionaldependencies_property_names = False if not data__dynamic__optionaldependencies_property_names: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be named by propertyName definition", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, rule='propertyNames') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.optional-dependencies must be named by propertyName definition", value=data__dynamic__optionaldependencies, name="" + (name_prefix or "data") + ".dynamic.optional-dependencies", definition={'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, rule='propertyNames') if "readme" in data__dynamic_keys: data__dynamic_keys.remove("readme") data__dynamic__readme = data__dynamic["readme"] + if not isinstance(data__dynamic__readme, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must be object", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}, rule='type') data__dynamic__readme_any_of_count6 = 0 if not data__dynamic__readme_any_of_count6: try: - validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data__dynamic__readme, custom_formats, (name_prefix or "data") + ".dynamic.readme") + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data__dynamic__readme, custom_formats, (name_prefix or "data") + ".dynamic.readme") data__dynamic__readme_any_of_count6 += 1 except JsonSchemaValueException: pass if not data__dynamic__readme_any_of_count6: try: + if not isinstance(data__dynamic__readme, (dict)): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must be object", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}, rule='type') data__dynamic__readme_is_dict = isinstance(data__dynamic__readme, dict) if data__dynamic__readme_is_dict: data__dynamic__readme_keys = set(data__dynamic__readme.keys()) @@ -494,41 +506,75 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html(data, data__dynamic__readme__contenttype = data__dynamic__readme["content-type"] if not isinstance(data__dynamic__readme__contenttype, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme.content-type must be string", value=data__dynamic__readme__contenttype, name="" + (name_prefix or "data") + ".dynamic.readme.content-type", definition={'type': 'string'}, rule='type') + if "file" in data__dynamic__readme_keys: + data__dynamic__readme_keys.remove("file") + data__dynamic__readme__file = data__dynamic__readme["file"] + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_properties_file(data__dynamic__readme__file, custom_formats, (name_prefix or "data") + ".dynamic.readme.file") + if data__dynamic__readme_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must not contain "+str(data__dynamic__readme_keys)+" properties", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}, rule='additionalProperties') data__dynamic__readme_any_of_count6 += 1 except JsonSchemaValueException: pass if not data__dynamic__readme_any_of_count6: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme cannot be validated by any definition", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}, rule='anyOf') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme cannot be validated by any definition", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}, rule='anyOf') data__dynamic__readme_is_dict = isinstance(data__dynamic__readme, dict) if data__dynamic__readme_is_dict: - data__dynamic__readme_len = len(data__dynamic__readme) - if not all(prop in data__dynamic__readme for prop in ['file']): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must contain ['file'] properties", value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}, rule='required') + data__dynamic__readme__missing_keys = set(['file']) - data__dynamic__readme.keys() + if data__dynamic__readme__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.readme must contain " + (str(sorted(data__dynamic__readme__missing_keys)) + " properties"), value=data__dynamic__readme, name="" + (name_prefix or "data") + ".dynamic.readme", definition={'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}, rule='required') if data__dynamic_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must not contain "+str(data__dynamic_keys)+" properties", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must not contain "+str(data__dynamic_keys)+" properties", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}, rule='additionalProperties') if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://setuptools.pypa.io/en/latest/references/keywords.html', 'title': '``tool.setuptools`` table', '$$description': ['Please notice for the time being the ``setuptools`` project does not specify', 'a way of configuring builds via ``pyproject.toml``.', 'Therefore this schema should be taken just as a *"thought experiment"* on how', 'this *might be done*, by following the principles established in', '`ini2toml `_.', 'It considers only ``setuptools`` `parameters', '`_', 'that can currently be configured via ``setup.cfg`` and are not covered by :pep:`621`', 'but intentionally excludes ``dependency_links`` and ``setup_requires``.', 'NOTE: ``scripts`` was renamed to ``script-files`` to avoid confusion with', 'entry-point based scripts (defined in :pep:`621`).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'description': 'Whether the project can be safely installed and run from a zip file.', 'type': 'boolean'}, 'script-files': {'description': 'Legacy way of defining scripts (entry-points are preferred).', 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'oneOf': [{'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['**DEPRECATED**: dict-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', "Please notice this don't work with wheels. See `data files support", '`_'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['PROVISIONAL: List of glob patterns for all license files being distributed.', '(might become standard with PEP 639).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'format': 'python-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}}}, 'readme': {'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'properties': {'content-type': {'type': 'string'}}}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html', 'title': '``tool.setuptools`` table', '$$description': ['``setuptools``-specific configurations that can be set by users that require', 'customization.', 'These configurations are completely optional and probably can be skipped when', 'creating simple packages. They are equivalent to some of the `Keywords', '`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '`_', 'that are not covered by :pep:`621`; and intentionally excludes ``dependency_links``', 'and ``setup_requires`` (incompatible with modern workflows/standards).'], 'type': 'object', 'additionalProperties': False, 'properties': {'platforms': {'type': 'array', 'items': {'type': 'string'}}, 'provides': {'$$description': ['Package and virtual package names contained within this package', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'obsoletes': {'$$description': ['Packages which this package renders obsolete', '**(not supported by pip)**'], 'type': 'array', 'items': {'type': 'string', 'format': 'pep508-identifier'}}, 'zip-safe': {'$$description': ['Whether the project can be safely installed and run from a zip file.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'boolean'}, 'script-files': {'$$description': ['Legacy way of defining scripts (entry-points are preferred).', 'Equivalent to the ``script`` keyword in ``setup.py``', '(it was renamed to avoid confusion with entry-point based ``project.scripts``', 'defined in :pep:`621`).', '**DISCOURAGED**: generic script wrappers are tricky and may not work properly.', 'Whenever possible, please use ``project.scripts`` instead.'], 'type': 'array', 'items': {'type': 'string'}, '$comment': 'TODO: is this field deprecated/should be removed?'}, 'eager-resources': {'$$description': ['Resources that should be extracted together, if any of them is needed,', 'or if any C extensions included in the project are imported.', '**OBSOLETE**: only relevant for ``pkg_resources``, ``easy_install`` and', '``setup.py install`` in the context of ``eggs`` (**DEPRECATED**).'], 'type': 'array', 'items': {'type': 'string'}}, 'packages': {'$$description': ['Packages that should be included in the distribution.', 'It can be given either as a list of package identifiers', 'or as a ``dict``-like structure with a single key ``find``', 'which corresponds to a dynamic call to', '``setuptools.config.expand.find_packages`` function.', 'The ``find`` key is associated with a nested ``dict``-like structure that can', 'contain ``where``, ``include``, ``exclude`` and ``namespaces`` keys,', 'mimicking the keyword arguments of the associated function.'], 'oneOf': [{'title': 'Array of Python package identifiers', 'type': 'array', 'items': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}]}, 'package-dir': {'$$description': [':class:`dict`-like structure mapping from package names to directories where their', 'code can be found.', 'The empty string (as key) means that all packages are contained inside', 'the given directory will be included in the distribution.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'const': ''}, {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, 'patternProperties': {'^.*$': {'type': 'string'}}}, 'package-data': {'$$description': ['Mapping from package names to lists of glob patterns.', 'Usually this option is not needed when using ``include-package-data = true``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'include-package-data': {'$$description': ['Automatically include any data files inside the package directories', 'that are specified by ``MANIFEST.in``', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'boolean'}, 'exclude-package-data': {'$$description': ['Mapping from package names to lists of glob patterns that should be excluded', 'For more information on how to include data files, check ``setuptools`` `docs', '`_.'], 'type': 'object', 'additionalProperties': False, 'propertyNames': {'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'const': '*'}]}, 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'namespace-packages': {'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'https://setuptools.pypa.io/en/latest/userguide/package_discovery.html', 'description': '**DEPRECATED**: use implicit namespaces instead (:pep:`420`).'}, 'py-modules': {'description': 'Modules that setuptools will manipulate', 'type': 'array', 'items': {'type': 'string', 'format': 'python-module-name'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'data-files': {'$$description': ['``dict``-like structure where each key represents a directory and', 'the value is a list of glob patterns that should be installed in them.', '**DISCOURAGED**: please notice this might not work as expected with wheels.', 'Whenever possible, consider using data files inside the package directories', '(or create a new namespace package that only contains data files).', 'See `data files support', '`_.'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'array', 'items': {'type': 'string'}}}}, 'cmdclass': {'$$description': ['Mapping of distutils-style command names to ``setuptools.Command`` subclasses', 'which in turn should be represented by strings with a qualified class name', '(i.e., "dotted" form with module), e.g.::\n\n', ' cmdclass = {mycmd = "pkg.subpkg.module.CommandClass"}\n\n', 'The command class should be a directly defined at the top-level of the', 'containing module (no class nesting).'], 'type': 'object', 'patternProperties': {'^.*$': {'type': 'string', 'format': 'python-qualified-identifier'}}}, 'license-files': {'type': 'array', 'items': {'type': 'string'}, '$$description': ['**PROVISIONAL**: list of glob patterns for all license files being distributed.', '(likely to become standard with :pep:`639`).', "By default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``"], '$comment': 'TODO: revise if PEP 639 is accepted. Probably ``project.license-files``?'}, 'dynamic': {'type': 'object', 'description': 'Instructions for loading :pep:`621`-related metadata dynamically', 'additionalProperties': False, 'properties': {'version': {'$$description': ['A version dynamically loaded via either the ``attr:`` or ``file:``', 'directives. Please make sure the given file or attribute respects :pep:`440`.', 'Also ensure to set ``project.dynamic`` accordingly.'], 'oneOf': [{'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'classifiers': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'description': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'entry-points': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}, 'optional-dependencies': {'type': 'object', 'propertyNames': {'type': 'string', 'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'.+': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$ref': '#/definitions/file-directive'}]}}}, 'readme': {'type': 'object', 'anyOf': [{'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, {'type': 'object', 'properties': {'content-type': {'type': 'string'}, 'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'additionalProperties': False}], 'required': ['file']}}}}, 'definitions': {'package-name': {'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'file-directive': {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, 'file-directive-for-dependencies': {'title': "'file:' directive for dependencies", 'allOf': [{'$$description': ['**BETA**: subset of the ``requirements.txt`` format', 'without ``pip`` flags and options', '(one :pep:`508`-compliant string per line,', 'lines that are blank or start with ``#`` are excluded).', 'See `dynamic metadata', '`_.']}, {'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}]}, 'attr-directive': {'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, 'find-directive': {'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}}}, rule='additionalProperties') return data -def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_file_directive(data, custom_formats={}, name_prefix=None): +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_properties_file(data, custom_formats={}, name_prefix=None): + data_one_of_count7 = 0 + if data_one_of_count7 < 2: + try: + if not isinstance(data, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string'}, rule='type') + data_one_of_count7 += 1 + except JsonSchemaValueException: pass + if data_one_of_count7 < 2: + try: + if not isinstance(data, (list, tuple)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be array", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') + data_is_list = isinstance(data, (list, tuple)) + if data_is_list: + data_len = len(data) + for data_x, data_item in enumerate(data): + if not isinstance(data_item, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + "[{data_x}]".format(**locals()) + " must be string", value=data_item, name="" + (name_prefix or "data") + "[{data_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') + data_one_of_count7 += 1 + except JsonSchemaValueException: pass + if data_one_of_count7 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be valid exactly by one definition" + (" (" + str(data_one_of_count7) + " matches found)"), value=data, name="" + (name_prefix or "data") + "", definition={'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}, rule='oneOf') + return data + +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive_for_dependencies(data, custom_formats={}, name_prefix=None): + validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data, custom_formats, (name_prefix or "data") + "") + return data + +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_file_directive(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: - data_len = len(data) - if not all(prop in data for prop in ['file']): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['file'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='required') + data__missing_keys = set(['file']) - data.keys() + if data__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='required') data_keys = set(data.keys()) if "file" in data_keys: data_keys.remove("file") data__file = data["file"] - data__file_one_of_count7 = 0 - if data__file_one_of_count7 < 2: + data__file_one_of_count8 = 0 + if data__file_one_of_count8 < 2: try: if not isinstance(data__file, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be string", value=data__file, name="" + (name_prefix or "data") + ".file", definition={'type': 'string'}, rule='type') - data__file_one_of_count7 += 1 + data__file_one_of_count8 += 1 except JsonSchemaValueException: pass - if data__file_one_of_count7 < 2: + if data__file_one_of_count8 < 2: try: if not isinstance(data__file, (list, tuple)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be array", value=data__file, name="" + (name_prefix or "data") + ".file", definition={'type': 'array', 'items': {'type': 'string'}}, rule='type') @@ -538,33 +584,36 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__defi for data__file_x, data__file_item in enumerate(data__file): if not isinstance(data__file_item, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".file[{data__file_x}]".format(**locals()) + " must be string", value=data__file_item, name="" + (name_prefix or "data") + ".file[{data__file_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type') - data__file_one_of_count7 += 1 + data__file_one_of_count8 += 1 except JsonSchemaValueException: pass - if data__file_one_of_count7 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be valid exactly by one definition" + (" (" + str(data__file_one_of_count7) + " matches found)"), value=data__file, name="" + (name_prefix or "data") + ".file", definition={'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}, rule='oneOf') + if data__file_one_of_count8 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be valid exactly by one definition" + (" (" + str(data__file_one_of_count8) + " matches found)"), value=data__file, name="" + (name_prefix or "data") + ".file", definition={'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}, rule='oneOf') if data_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/file-directive', 'title': "'file:' directive", 'description': 'Value is read from a file (or list of files and then concatenated)', 'type': 'object', 'additionalProperties': False, 'properties': {'file': {'oneOf': [{'type': 'string'}, {'type': 'array', 'items': {'type': 'string'}}]}}, 'required': ['file']}, rule='additionalProperties') return data -def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_attr_directive(data, custom_formats={}, name_prefix=None): +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_attr_directive(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: - data_len = len(data) - if not all(prop in data for prop in ['attr']): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['attr'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, rule='required') + data__missing_keys = set(['attr']) - data.keys() + if data__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, rule='required') data_keys = set(data.keys()) if "attr" in data_keys: data_keys.remove("attr") data__attr = data["attr"] if not isinstance(data__attr, (str)): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".attr must be string", value=data__attr, name="" + (name_prefix or "data") + ".attr", definition={'type': 'string'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + ".attr must be string", value=data__attr, name="" + (name_prefix or "data") + ".attr", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='type') + if isinstance(data__attr, str): + if not custom_formats["python-qualified-identifier"](data__attr): + raise JsonSchemaValueException("" + (name_prefix or "data") + ".attr must be python-qualified-identifier", value=data__attr, name="" + (name_prefix or "data") + ".attr", definition={'type': 'string', 'format': 'python-qualified-identifier'}, rule='format') if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string'}}, 'required': ['attr']}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'title': "'attr:' directive", '$id': '#/definitions/attr-directive', '$$description': ['Value is read from a module attribute. Supports callables and iterables;', 'unsupported types are cast via ``str()``'], 'type': 'object', 'additionalProperties': False, 'properties': {'attr': {'type': 'string', 'format': 'python-qualified-identifier'}}, 'required': ['attr']}, rule='additionalProperties') return data -def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_find_directive(data, custom_formats={}, name_prefix=None): +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_find_directive(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='type') data_is_dict = isinstance(data, dict) @@ -622,31 +671,35 @@ def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__defi raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/find-directive', 'title': "'find:' directive", 'type': 'object', 'additionalProperties': False, 'properties': {'find': {'type': 'object', '$$description': ['Dynamic `package discovery', '`_.'], 'additionalProperties': False, 'properties': {'where': {'description': 'Directories to be searched for packages (Unix-style relative path)', 'type': 'array', 'items': {'type': 'string'}}, 'exclude': {'type': 'array', '$$description': ['Exclude packages that match the values listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'include': {'type': 'array', '$$description': ['Restrict the found packages to just the ones listed in this field.', "Can container shell-style wildcards (e.g. ``'pkg.*'``)"], 'items': {'type': 'string'}}, 'namespaces': {'type': 'boolean', '$$description': ['When ``True``, directories without a ``__init__.py`` file will also', 'be scanned for :pep:`420`-style implicit namespaces']}}}}}, rule='additionalProperties') return data -def validate_https___setuptools_pypa_io_en_latest_references_keywords_html__definitions_package_name(data, custom_formats={}, name_prefix=None): +def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_html__definitions_package_name(data, custom_formats={}, name_prefix=None): if not isinstance(data, (str)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, rule='type') - data_any_of_count8 = 0 - if not data_any_of_count8: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='type') + data_any_of_count9 = 0 + if not data_any_of_count9: try: + if not isinstance(data, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='type') if isinstance(data, str): if not custom_formats["python-module-name"](data): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name", value=data, name="" + (name_prefix or "data") + "", definition={'format': 'python-module-name'}, rule='format') - data_any_of_count8 += 1 + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name'}, rule='format') + data_any_of_count9 += 1 except JsonSchemaValueException: pass - if not data_any_of_count8: + if not data_any_of_count9: try: + if not isinstance(data, (str)): + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'pep561-stub-name'}, rule='type') if isinstance(data, str): if not custom_formats["pep561-stub-name"](data): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep561-stub-name", value=data, name="" + (name_prefix or "data") + "", definition={'format': 'pep561-stub-name'}, rule='format') - data_any_of_count8 += 1 + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep561-stub-name", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'pep561-stub-name'}, rule='format') + data_any_of_count9 += 1 except JsonSchemaValueException: pass - if not data_any_of_count8: - raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or PEP 561).', 'type': 'string', 'anyOf': [{'format': 'python-module-name'}, {'format': 'pep561-stub-name'}]}, rule='anyOf') + if not data_any_of_count9: + raise JsonSchemaValueException("" + (name_prefix or "data") + " cannot be validated by any definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/package-name', 'title': 'Valid package name', 'description': 'Valid package name (importable or :pep:`561`).', 'type': 'string', 'anyOf': [{'type': 'string', 'format': 'python-module-name'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='anyOf') return data -def validate_https___docs_python_org_3_install(data, custom_formats={}, name_prefix=None): +def validate_https___setuptools_pypa_io_en_latest_deprecated_distutils_configfile_html(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://docs.python.org/3/install/', 'title': '``tool.distutils`` table', '$$description': ['Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` scripts via `distutils configuration files', '`_.', '``tool.distutils`` subtables could be used with the same purpose', '(NOT CURRENTLY IMPLEMENTED).'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html', 'title': '``tool.distutils`` table', '$$description': ['**EXPERIMENTAL** (NOT OFFICIALLY SUPPORTED): Use ``tool.distutils``', 'subtables to configure arguments for ``distutils`` commands.', 'Originally, ``distutils`` allowed developers to configure arguments for', '``setup.py`` commands via `distutils configuration files', '`_.', 'See also `the old Python docs _`.'], 'type': 'object', 'properties': {'global': {'type': 'object', 'description': 'Global options applied to all ``distutils`` commands'}}, 'patternProperties': {'.+': {'type': 'object'}}, '$comment': 'TODO: Is there a practical way of making this schema more specific?'}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: data_keys = set(data.keys()) @@ -663,14 +716,14 @@ def validate_https___docs_python_org_3_install(data, custom_formats={}, name_pre raise JsonSchemaValueException("" + (name_prefix or "data") + ".{data_key}".format(**locals()) + " must be object", value=data_val, name="" + (name_prefix or "data") + ".{data_key}".format(**locals()) + "", definition={'type': 'object'}, rule='type') return data -def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata(data, custom_formats={}, name_prefix=None): +def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='type') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='type') data_is_dict = isinstance(data, dict) if data_is_dict: - data_len = len(data) - if not all(prop in data for prop in ['name']): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['name'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='required') + data__missing_keys = set(['name']) - data.keys() + if data__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='required') data_keys = set(data.keys()) if "name" in data_keys: data_keys.remove("name") @@ -696,65 +749,65 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if "readme" in data_keys: data_keys.remove("readme") data__readme = data["readme"] - data__readme_one_of_count9 = 0 - if data__readme_one_of_count9 < 2: + data__readme_one_of_count10 = 0 + if data__readme_one_of_count10 < 2: try: if not isinstance(data__readme, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be string", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, rule='type') - data__readme_one_of_count9 += 1 + data__readme_one_of_count10 += 1 except JsonSchemaValueException: pass - if data__readme_one_of_count9 < 2: + if data__readme_one_of_count10 < 2: try: if not isinstance(data__readme, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be object", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}, rule='type') - data__readme_any_of_count10 = 0 - if not data__readme_any_of_count10: + data__readme_any_of_count11 = 0 + if not data__readme_any_of_count11: try: data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: - data__readme_len = len(data__readme) - if not all(prop in data__readme for prop in ['file']): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain ['file'] properties", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, rule='required') + data__readme__missing_keys = set(['file']) - data__readme.keys() + if data__readme__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain " + (str(sorted(data__readme__missing_keys)) + " properties"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, rule='required') data__readme_keys = set(data__readme.keys()) if "file" in data__readme_keys: data__readme_keys.remove("file") data__readme__file = data__readme["file"] if not isinstance(data__readme__file, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.file must be string", value=data__readme__file, name="" + (name_prefix or "data") + ".readme.file", definition={'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}, rule='type') - data__readme_any_of_count10 += 1 + data__readme_any_of_count11 += 1 except JsonSchemaValueException: pass - if not data__readme_any_of_count10: + if not data__readme_any_of_count11: try: data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: - data__readme_len = len(data__readme) - if not all(prop in data__readme for prop in ['text']): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain ['text'] properties", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}, rule='required') + data__readme__missing_keys = set(['text']) - data__readme.keys() + if data__readme__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain " + (str(sorted(data__readme__missing_keys)) + " properties"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}, rule='required') data__readme_keys = set(data__readme.keys()) if "text" in data__readme_keys: data__readme_keys.remove("text") data__readme__text = data__readme["text"] if not isinstance(data__readme__text, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.text must be string", value=data__readme__text, name="" + (name_prefix or "data") + ".readme.text", definition={'type': 'string', 'description': 'Full text describing the project.'}, rule='type') - data__readme_any_of_count10 += 1 + data__readme_any_of_count11 += 1 except JsonSchemaValueException: pass - if not data__readme_any_of_count10: + if not data__readme_any_of_count11: raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme cannot be validated by any definition", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, rule='anyOf') data__readme_is_dict = isinstance(data__readme, dict) if data__readme_is_dict: - data__readme_len = len(data__readme) - if not all(prop in data__readme for prop in ['content-type']): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain ['content-type'] properties", value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}, rule='required') + data__readme__missing_keys = set(['content-type']) - data__readme.keys() + if data__readme__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must contain " + (str(sorted(data__readme__missing_keys)) + " properties"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}, rule='required') data__readme_keys = set(data__readme.keys()) if "content-type" in data__readme_keys: data__readme_keys.remove("content-type") data__readme__contenttype = data__readme["content-type"] if not isinstance(data__readme__contenttype, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme.content-type must be string", value=data__readme__contenttype, name="" + (name_prefix or "data") + ".readme.content-type", definition={'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}, rule='type') - data__readme_one_of_count9 += 1 + data__readme_one_of_count10 += 1 except JsonSchemaValueException: pass - if data__readme_one_of_count9 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count9) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf') + if data__readme_one_of_count10 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count10) + " matches found)"), value=data__readme, name="" + (name_prefix or "data") + ".readme", definition={'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, rule='oneOf') if "requires-python" in data_keys: data_keys.remove("requires-python") data__requirespython = data["requires-python"] @@ -766,39 +819,39 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if "license" in data_keys: data_keys.remove("license") data__license = data["license"] - data__license_one_of_count11 = 0 - if data__license_one_of_count11 < 2: + data__license_one_of_count12 = 0 + if data__license_one_of_count12 < 2: try: data__license_is_dict = isinstance(data__license, dict) if data__license_is_dict: - data__license_len = len(data__license) - if not all(prop in data__license for prop in ['file']): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain ['file'] properties", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, rule='required') + data__license__missing_keys = set(['file']) - data__license.keys() + if data__license__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain " + (str(sorted(data__license__missing_keys)) + " properties"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, rule='required') data__license_keys = set(data__license.keys()) if "file" in data__license_keys: data__license_keys.remove("file") data__license__file = data__license["file"] if not isinstance(data__license__file, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.file must be string", value=data__license__file, name="" + (name_prefix or "data") + ".license.file", definition={'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}, rule='type') - data__license_one_of_count11 += 1 + data__license_one_of_count12 += 1 except JsonSchemaValueException: pass - if data__license_one_of_count11 < 2: + if data__license_one_of_count12 < 2: try: data__license_is_dict = isinstance(data__license, dict) if data__license_is_dict: - data__license_len = len(data__license) - if not all(prop in data__license for prop in ['text']): - raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain ['text'] properties", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}, rule='required') + data__license__missing_keys = set(['text']) - data__license.keys() + if data__license__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must contain " + (str(sorted(data__license__missing_keys)) + " properties"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}, rule='required') data__license_keys = set(data__license.keys()) if "text" in data__license_keys: data__license_keys.remove("text") data__license__text = data__license["text"] if not isinstance(data__license__text, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + ".license.text must be string", value=data__license__text, name="" + (name_prefix or "data") + ".license.text", definition={'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}, rule='type') - data__license_one_of_count11 += 1 + data__license_one_of_count12 += 1 except JsonSchemaValueException: pass - if data__license_one_of_count11 != 1: - raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count11) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, rule='oneOf') + if data__license_one_of_count12 != 1: + raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count12) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, rule='oneOf') if "authors" in data_keys: data_keys.remove("authors") data__authors = data["authors"] @@ -808,7 +861,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__authors_is_list: data__authors_len = len(data__authors) for data__authors_x, data__authors_item in enumerate(data__authors): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__authors_item, custom_formats, (name_prefix or "data") + ".authors[{data__authors_x}]".format(**locals())) + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_author(data__authors_item, custom_formats, (name_prefix or "data") + ".authors[{data__authors_x}]".format(**locals())) if "maintainers" in data_keys: data_keys.remove("maintainers") data__maintainers = data["maintainers"] @@ -818,7 +871,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__maintainers_is_list: data__maintainers_len = len(data__maintainers) for data__maintainers_x, data__maintainers_item in enumerate(data__maintainers): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data__maintainers_item, custom_formats, (name_prefix or "data") + ".maintainers[{data__maintainers_x}]".format(**locals())) + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_author(data__maintainers_item, custom_formats, (name_prefix or "data") + ".maintainers[{data__maintainers_x}]".format(**locals())) if "keywords" in data_keys: data_keys.remove("keywords") data__keywords = data["keywords"] @@ -866,11 +919,11 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if "scripts" in data_keys: data_keys.remove("scripts") data__scripts = data["scripts"] - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__scripts, custom_formats, (name_prefix or "data") + ".scripts") + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data__scripts, custom_formats, (name_prefix or "data") + ".scripts") if "gui-scripts" in data_keys: data_keys.remove("gui-scripts") data__guiscripts = data["gui-scripts"] - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__guiscripts, custom_formats, (name_prefix or "data") + ".gui-scripts") + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data__guiscripts, custom_formats, (name_prefix or "data") + ".gui-scripts") if "entry-points" in data_keys: data_keys.remove("entry-points") data__entrypoints = data["entry-points"] @@ -881,7 +934,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if REGEX_PATTERNS['^.+$'].search(data__entrypoints_key): if data__entrypoints_key in data__entrypoints_keys: data__entrypoints_keys.remove(data__entrypoints_key) - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data__entrypoints_val, custom_formats, (name_prefix or "data") + ".entry-points.{data__entrypoints_key}".format(**locals())) + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data__entrypoints_val, custom_formats, (name_prefix or "data") + ".entry-points.{data__entrypoints_key}".format(**locals())) if data__entrypoints_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + ".entry-points must not contain "+str(data__entrypoints_keys)+" properties", value=data__entrypoints, name="" + (name_prefix or "data") + ".entry-points", definition={'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, rule='additionalProperties') data__entrypoints_len = len(data__entrypoints) @@ -905,7 +958,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__dependencies_is_list: data__dependencies_len = len(data__dependencies) for data__dependencies_x, data__dependencies_item in enumerate(data__dependencies): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__dependencies_item, custom_formats, (name_prefix or "data") + ".dependencies[{data__dependencies_x}]".format(**locals())) + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_dependency(data__dependencies_item, custom_formats, (name_prefix or "data") + ".dependencies[{data__dependencies_x}]".format(**locals())) if "optional-dependencies" in data_keys: data_keys.remove("optional-dependencies") data__optionaldependencies = data["optional-dependencies"] @@ -924,7 +977,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__optionaldependencies_val_is_list: data__optionaldependencies_val_len = len(data__optionaldependencies_val) for data__optionaldependencies_val_x, data__optionaldependencies_val_item in enumerate(data__optionaldependencies_val): - validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data__optionaldependencies_val_item, custom_formats, (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}[{data__optionaldependencies_val_x}]".format(**locals())) + validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_dependency(data__optionaldependencies_val_item, custom_formats, (name_prefix or "data") + ".optional-dependencies.{data__optionaldependencies_key}[{data__optionaldependencies_val_x}]".format(**locals())) if data__optionaldependencies_keys: raise JsonSchemaValueException("" + (name_prefix or "data") + ".optional-dependencies must not contain "+str(data__optionaldependencies_keys)+" properties", value=data__optionaldependencies, name="" + (name_prefix or "data") + ".optional-dependencies", definition={'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, rule='additionalProperties') data__optionaldependencies_len = len(data__optionaldependencies) @@ -951,14 +1004,14 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro if data__dynamic_item not in ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']: raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + " must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']", value=data__dynamic_item, name="" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + "", definition={'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}, rule='enum') if data_keys: - raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema', '$id': 'https://packaging.python.org/en/latest/specifications/declaring-project-metadata/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='additionalProperties') + raise JsonSchemaValueException("" + (name_prefix or "data") + " must not contain "+str(data_keys)+" properties", value=data, name="" + (name_prefix or "data") + "", definition={'$schema': 'http://json-schema.org/draft-07/schema#', '$id': 'https://packaging.python.org/en/latest/specifications/pyproject-toml/', 'title': 'Package metadata stored in the ``project`` table', '$$description': ['Data structure for the **project** table inside ``pyproject.toml``', '(as initially defined in :pep:`621`)'], 'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name (primary identifier) of the project. MUST be statically defined.', 'format': 'pep508-identifier'}, 'version': {'type': 'string', 'description': 'The version of the project as supported by :pep:`440`.', 'format': 'pep440'}, 'description': {'type': 'string', '$$description': ['The `summary description of the project', '`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '`_', "with meaning similar to the one defined in `core metadata's Description", '`_'], 'oneOf': [{'type': 'string', '$$description': ['Relative path to a text file (UTF-8) containing the full description', 'of the project. If the file path ends in case-insensitive ``.md`` or', '``.rst`` suffixes, then the content-type is respectively', '``text/markdown`` or ``text/x-rst``']}, {'type': 'object', 'allOf': [{'anyOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to a text file containing the full description', 'of the project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', 'description': 'Full text describing the project.'}}, 'required': ['text']}]}, {'properties': {'content-type': {'type': 'string', '$$description': ['Content-type (:rfc:`1341`) of the full description', '(e.g. ``text/markdown``). The ``charset`` parameter is assumed', 'UTF-8 when not present.'], '$comment': 'TODO: add regex pattern or format?'}}, 'required': ['content-type']}]}]}, 'requires-python': {'type': 'string', 'format': 'pep508-versionspec', '$$description': ['`The Python version requirements of the project', '`_.']}, 'license': {'description': '`Project license `_.', 'oneOf': [{'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '`_.']}}, 'required': ['text']}]}, 'authors': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'authors' of the project.", 'The exact meaning is open to interpretation (e.g. original or primary authors,', 'current maintainers, or owners of the package).']}, 'maintainers': {'type': 'array', 'items': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, '$$description': ["The people or organizations considered to be the 'maintainers' of the project.", 'Similarly to ``authors``, the exact meaning is open to interpretation.']}, 'keywords': {'type': 'array', 'items': {'type': 'string'}, 'description': 'List of keywords to assist searching for the distribution in a larger catalog.'}, 'classifiers': {'type': 'array', 'items': {'type': 'string', 'format': 'trove-classifier', 'description': '`PyPI classifier `_.'}, '$$description': ['`Trove classifiers `_', 'which apply to the project.']}, 'urls': {'type': 'object', 'description': 'URLs associated with the project in the form ``label => value``.', 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', 'format': 'url'}}}, 'scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'gui-scripts': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'entry-points': {'$$description': ['Instruct the installer to expose the given modules/functions via', '``entry-point`` discovery mechanism (useful for plugins).', 'More information available in the `Python packaging guide', '`_.'], 'propertyNames': {'format': 'python-entrypoint-group'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}}}, 'dependencies': {'type': 'array', 'description': 'Project (mandatory) dependencies.', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}, 'optional-dependencies': {'type': 'object', 'description': 'Optional dependency for the project', 'propertyNames': {'format': 'pep508-identifier'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'array', 'items': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}}, 'dynamic': {'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'if': {'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, 'then': {'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, 'definitions': {'author': {'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, 'entry-point-group': {'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, 'dependency': {'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}}}, rule='additionalProperties') try: try: data_is_dict = isinstance(data, dict) if data_is_dict: - data_len = len(data) - if not all(prop in data for prop in ['dynamic']): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['dynamic'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, rule='required') + data__missing_keys = set(['dynamic']) - data.keys() + if data__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, rule='required') data_keys = set(data.keys()) if "dynamic" in data_keys: data_keys.remove("dynamic") @@ -983,12 +1036,12 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro else: data_is_dict = isinstance(data, dict) if data_is_dict: - data_len = len(data) - if not all(prop in data for prop in ['version']): - raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain ['version'] properties", value=data, name="" + (name_prefix or "data") + "", definition={'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, rule='required') + data__missing_keys = set(['version']) - data.keys() + if data__missing_keys: + raise JsonSchemaValueException("" + (name_prefix or "data") + " must contain " + (str(sorted(data__missing_keys)) + " properties"), value=data, name="" + (name_prefix or "data") + "", definition={'required': ['version'], '$$description': ['version should be statically defined in the ``version`` field']}, rule='required') return data -def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_dependency(data, custom_formats={}, name_prefix=None): +def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_dependency(data, custom_formats={}, name_prefix=None): if not isinstance(data, (str)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be string", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}, rule='type') if isinstance(data, str): @@ -996,7 +1049,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro raise JsonSchemaValueException("" + (name_prefix or "data") + " must be pep508", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/dependency', 'title': 'Dependency', 'type': 'string', 'description': 'Project dependency specification according to PEP 508', 'format': 'pep508'}, rule='format') return data -def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_entry_point_group(data, custom_formats={}, name_prefix=None): +def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_entry_point_group(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='type') data_is_dict = isinstance(data, dict) @@ -1027,7 +1080,7 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_pro raise JsonSchemaValueException("" + (name_prefix or "data") + " must be named by propertyName definition", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/entry-point-group', 'title': 'Entry-points', 'type': 'object', '$$description': ['Entry-points are grouped together to indicate what sort of capabilities they', 'provide.', 'See the `packaging guides', '`_', 'and `setuptools docs', '`_', 'for more information.'], 'propertyNames': {'format': 'python-entrypoint-name'}, 'additionalProperties': False, 'patternProperties': {'^.+$': {'type': 'string', '$$description': ['Reference to a Python object. It is either in the form', '``importable.module``, or ``importable.module:object.attr``.'], 'format': 'python-entrypoint-reference', '$comment': 'https://packaging.python.org/specifications/entry-points/'}}}, rule='propertyNames') return data -def validate_https___packaging_python_org_en_latest_specifications_declaring_project_metadata___definitions_author(data, custom_formats={}, name_prefix=None): +def validate_https___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_author(data, custom_formats={}, name_prefix=None): if not isinstance(data, (dict)): raise JsonSchemaValueException("" + (name_prefix or "data") + " must be object", value=data, name="" + (name_prefix or "data") + "", definition={'$id': '#/definitions/author', 'title': 'Author or Maintainer', '$comment': 'https://peps.python.org/pep-0621/#authors-maintainers', 'type': 'object', 'additionalProperties': False, 'properties': {'name': {'type': 'string', '$$description': ['MUST be a valid email name, i.e. whatever can be put as a name, before an', 'email, in :rfc:`822`.']}, 'email': {'type': 'string', 'format': 'idn-email', 'description': 'MUST be a valid email address'}}}, rule='type') data_is_dict = isinstance(data, dict) diff --git a/setuptools/config/_validate_pyproject/formats.py b/setuptools/config/_validate_pyproject/formats.py index e73961661d..5a0599cbb5 100644 --- a/setuptools/config/_validate_pyproject/formats.py +++ b/setuptools/config/_validate_pyproject/formats.py @@ -1,3 +1,13 @@ +""" +The functions in this module are used to validate schemas with the +`format JSON Schema keyword +`_. + +The correspondence is given by replacing the ``_`` character in the name of the +function with a ``-`` to obtain the format name and vice versa. +""" + +import builtins import logging import os import re @@ -20,7 +30,7 @@ (?P[0-9]+(?:\.[0-9]+)*) # release segment (?P
                                          # pre-release
             [-_\.]?
-            (?P(a|b|c|rc|alpha|beta|pre|preview))
+            (?Palpha|a|beta|b|preview|pre|c|rc)
             [-_\.]?
             (?P[0-9]+)?
         )?
@@ -48,6 +58,9 @@
 
 
 def pep440(version: str) -> bool:
+    """See :ref:`PyPA's version specification `
+    (initially introduced in :pep:`440`).
+    """
     return VERSION_REGEX.match(version) is not None
 
 
@@ -59,6 +72,9 @@ def pep440(version: str) -> bool:
 
 
 def pep508_identifier(name: str) -> bool:
+    """See :ref:`PyPA's name specification `
+    (initially introduced in :pep:`508#names`).
+    """
     return PEP508_IDENTIFIER_REGEX.match(name) is not None
 
 
@@ -70,6 +86,9 @@ def pep508_identifier(name: str) -> bool:
         from setuptools._vendor.packaging import requirements as _req  # type: ignore
 
     def pep508(value: str) -> bool:
+        """See :ref:`PyPA's dependency specifiers `
+        (initially introduced in :pep:`508`).
+        """
         try:
             _req.Requirement(value)
             return True
@@ -88,7 +107,10 @@ def pep508(value: str) -> bool:
 
 
 def pep508_versionspec(value: str) -> bool:
-    """Expression that can be used to specify/lock versions (including ranges)"""
+    """Expression that can be used to specify/lock versions (including ranges)
+    See ``versionspec`` in :ref:`PyPA's dependency specifiers
+    ` (initially introduced in :pep:`508`).
+    """
     if any(c in value for c in (";", "]", "@")):
         # In PEP 508:
         # conditional markers, extras and URL specs are not included in the
@@ -104,6 +126,11 @@ def pep508_versionspec(value: str) -> bool:
 
 
 def pep517_backend_reference(value: str) -> bool:
+    """See PyPA's specification for defining build-backend references
+    introduced in :pep:`517#source-trees`.
+
+    This is similar to an entry-point reference (e.g., ``package.module:object``).
+    """
     module, _, obj = value.partition(":")
     identifiers = (i.strip() for i in _chain(module.split("."), obj.split(".")))
     return all(python_identifier(i) for i in identifiers if i)
@@ -120,10 +147,10 @@ def _download_classifiers() -> str:
 
     url = "https://pypi.org/pypi?:action=list_classifiers"
     context = ssl.create_default_context()
-    with urlopen(url, context=context) as response:
+    with urlopen(url, context=context) as response:  # noqa: S310 (audit URLs)
         headers = Message()
         headers["content_type"] = response.getheader("content-type", "text/plain")
-        return response.read().decode(headers.get_param("charset", "utf-8"))
+        return response.read().decode(headers.get_param("charset", "utf-8"))  # type: ignore[no-any-return]
 
 
 class _TroveClassifier:
@@ -136,14 +163,14 @@ class _TroveClassifier:
 
     downloaded: typing.Union[None, "Literal[False]", typing.Set[str]]
 
-    def __init__(self):
+    def __init__(self) -> None:
         self.downloaded = None
         self._skip_download = False
         # None => not cached yet
         # False => cache not available
         self.__name__ = "trove_classifier"  # Emulate a public function
 
-    def _disable_download(self):
+    def _disable_download(self) -> None:
         # This is a private API. Only setuptools has the consent of using it.
         self._skip_download = True
 
@@ -180,6 +207,7 @@ def __call__(self, value: str) -> bool:
     from trove_classifiers import classifiers as _trove_classifiers
 
     def trove_classifier(value: str) -> bool:
+        """See https://pypi.org/classifiers/"""
         return value in _trove_classifiers or value.lower().startswith("private ::")
 
 except ImportError:  # pragma: no cover
@@ -191,6 +219,10 @@ def trove_classifier(value: str) -> bool:
 
 
 def pep561_stub_name(value: str) -> bool:
+    """Name of a directory containing type stubs.
+    It must follow the name scheme ``-stubs`` as defined in
+    :pep:`561#stub-only-packages`.
+    """
     top, *children = value.split(".")
     if not top.endswith("-stubs"):
         return False
@@ -202,6 +234,10 @@ def pep561_stub_name(value: str) -> bool:
 
 
 def url(value: str) -> bool:
+    """Valid URL (validation uses :obj:`urllib.parse`).
+    For maximum compatibility please make sure to include a ``scheme`` prefix
+    in your URL (e.g. ``http://``).
+    """
     from urllib.parse import urlparse
 
     try:
@@ -230,24 +266,40 @@ def url(value: str) -> bool:
 
 
 def python_identifier(value: str) -> bool:
+    """Can be used as identifier in Python.
+    (Validation uses :obj:`str.isidentifier`).
+    """
     return value.isidentifier()
 
 
 def python_qualified_identifier(value: str) -> bool:
+    """
+    Python "dotted identifier", i.e. a sequence of :obj:`python_identifier`
+    concatenated with ``"."`` (e.g.: ``package.module.submodule``).
+    """
     if value.startswith(".") or value.endswith("."):
         return False
     return all(python_identifier(m) for m in value.split("."))
 
 
 def python_module_name(value: str) -> bool:
+    """Module name that can be used in an ``import``-statement in Python.
+    See :obj:`python_qualified_identifier`.
+    """
     return python_qualified_identifier(value)
 
 
 def python_entrypoint_group(value: str) -> bool:
+    """See ``Data model > group`` in the :ref:`PyPA's entry-points specification
+    `.
+    """
     return ENTRYPOINT_GROUP_REGEX.match(value) is not None
 
 
 def python_entrypoint_name(value: str) -> bool:
+    """See ``Data model > name`` in the :ref:`PyPA's entry-points specification
+    `.
+    """
     if not ENTRYPOINT_REGEX.match(value):
         return False
     if not RECOMMEDED_ENTRYPOINT_REGEX.match(value):
@@ -258,6 +310,13 @@ def python_entrypoint_name(value: str) -> bool:
 
 
 def python_entrypoint_reference(value: str) -> bool:
+    """Reference to a Python object using in the format::
+
+        importable.module:object.attr
+
+    See ``Data model >object reference`` in the :ref:`PyPA's entry-points specification
+    `.
+    """
     module, _, rest = value.partition(":")
     if "[" in rest:
         obj, _, extras_ = rest.partition("[")
@@ -273,3 +332,23 @@ def python_entrypoint_reference(value: str) -> bool:
     module_parts = module.split(".")
     identifiers = _chain(module_parts, obj.split(".")) if rest else module_parts
     return all(python_identifier(i.strip()) for i in identifiers)
+
+
+def uint8(value: builtins.int) -> bool:
+    r"""Unsigned 8-bit integer (:math:`0 \leq x < 2^8`)"""
+    return 0 <= value < 2**8
+
+
+def uint16(value: builtins.int) -> bool:
+    r"""Unsigned 16-bit integer (:math:`0 \leq x < 2^{16}`)"""
+    return 0 <= value < 2**16
+
+
+def uint(value: builtins.int) -> bool:
+    r"""Unsigned 64-bit integer (:math:`0 \leq x < 2^{64}`)"""
+    return 0 <= value < 2**64
+
+
+def int(value: builtins.int) -> bool:
+    r"""Signed 64-bit integer (:math:`-2^{63} \leq x < 2^{63}`)"""
+    return -(2**63) <= value < 2**63

From 0576d60a00519a3c677de98f449b0a536c1630d7 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Tue, 7 May 2024 19:02:27 +0100
Subject: [PATCH 101/135] Add exception to mypy.ini

---
 mypy.ini | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/mypy.ini b/mypy.ini
index 45671826b1..9fab958288 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -34,3 +34,8 @@ ignore_missing_imports = True
 # - setuptools._vendor.packaging._manylinux: Mypy issue, this vendored module is already excluded!
 [mypy-pkg_resources.tests.*,setuptools._vendor.packaging._manylinux,setuptools.config._validate_pyproject.*]
 disable_error_code = import-not-found
+
+# - The unused-ignore comment in setuptools.config._validate_pyproject.* is probably evaluated differently
+#   in different versions of Python. Also, this file should already be ignored...
+[mypy-setuptools.config._validate_pyproject.*]
+disable_error_code = unused-ignore

From 1a1d7a6f02a5e17db9d14e6771542afe1b8de70b Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Tue, 7 May 2024 15:17:31 -0400
Subject: [PATCH 102/135] Simplified _declare_state

---
 pkg_resources/__init__.py | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index d3213b3237..63b0bf0bd4 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -117,11 +117,11 @@ class PEP440Warning(RuntimeWarning):
 parse_version = packaging.version.Version
 
 
-_state_vars: Dict[str, Any] = {}
+_state_vars: Dict[str, str] = {}
 
 
-def _declare_state(vartype: str, **kw: object) -> None:
-    _state_vars.update(dict.fromkeys(kw, vartype))
+def _declare_state(vartype: str, varname: str) -> None:
+    _state_vars[varname] = vartype
 
 
 def __getstate__():
@@ -2024,7 +2024,7 @@ def __init__(self, importer):
 _distribution_finders: Dict[
     type, Callable[[object, str, bool], Iterable["Distribution"]]
 ] = {}
-_declare_state('dict', _distribution_finders=_distribution_finders)
+_declare_state('dict', '_distribution_finders')
 
 
 def register_finder(importer_type, distribution_finder):
@@ -2200,9 +2200,9 @@ def resolve_egg_link(path):
 _namespace_handlers: Dict[
     type, Callable[[object, str, str, types.ModuleType], Optional[str]]
 ] = {}
-_declare_state('dict', _namespace_handlers=_namespace_handlers)
+_declare_state('dict', '_namespace_handlers')
 _namespace_packages: Dict[Optional[str], List[str]] = {}
-_declare_state('dict', _namespace_packages=_namespace_packages)
+_declare_state('dict', '_namespace_packages')
 
 
 def register_namespace_handler(importer_type, namespace_handler):
@@ -3302,7 +3302,7 @@ def _initialize_master_working_set():
     at their own risk.
     """
     working_set = WorkingSet._build_master()
-    _declare_state('object', working_set=working_set)
+    _declare_state('object', 'working_set')
 
     require = working_set.require
     iter_entry_points = working_set.iter_entry_points

From 47e8a11bcfe6eaea7470691a9bc3a10665c2a82b Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Tue, 7 May 2024 15:19:33 -0400
Subject: [PATCH 103/135] Include initial_value to _declare_state

---
 pkg_resources/__init__.py | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 63b0bf0bd4..a66e701a3d 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -27,7 +27,7 @@
 import time
 import re
 import types
-from typing import Any, Callable, Dict, Iterable, List, Protocol, Optional
+from typing import Any, Callable, Dict, Iterable, List, Protocol, Optional, TypeVar
 import zipfile
 import zipimport
 import warnings
@@ -103,6 +103,8 @@
     stacklevel=2,
 )
 
+T = TypeVar("T")
+
 
 _PEP440_FALLBACK = re.compile(r"^v?(?P(?:[0-9]+!)?[0-9]+(?:\.[0-9]+)*)", re.I)
 
@@ -120,8 +122,9 @@ class PEP440Warning(RuntimeWarning):
 _state_vars: Dict[str, str] = {}
 
 
-def _declare_state(vartype: str, varname: str) -> None:
+def _declare_state(vartype: str, varname: str, initial_value: T) -> T:
     _state_vars[varname] = vartype
+    return initial_value
 
 
 def __getstate__():
@@ -2023,8 +2026,7 @@ def __init__(self, importer):
 
 _distribution_finders: Dict[
     type, Callable[[object, str, bool], Iterable["Distribution"]]
-] = {}
-_declare_state('dict', '_distribution_finders')
+] = _declare_state('dict', '_distribution_finders', {})
 
 
 def register_finder(importer_type, distribution_finder):
@@ -2199,10 +2201,10 @@ def resolve_egg_link(path):
 
 _namespace_handlers: Dict[
     type, Callable[[object, str, str, types.ModuleType], Optional[str]]
-] = {}
-_declare_state('dict', '_namespace_handlers')
-_namespace_packages: Dict[Optional[str], List[str]] = {}
-_declare_state('dict', '_namespace_packages')
+] = _declare_state('dict', '_namespace_handlers', {})
+_namespace_packages: Dict[Optional[str], List[str]] = _declare_state(
+    'dict', '_namespace_packages', {}
+)
 
 
 def register_namespace_handler(importer_type, namespace_handler):
@@ -3301,8 +3303,7 @@ def _initialize_master_working_set():
     Invocation by other packages is unsupported and done
     at their own risk.
     """
-    working_set = WorkingSet._build_master()
-    _declare_state('object', 'working_set')
+    working_set = _declare_state('object', 'working_set', WorkingSet._build_master())
 
     require = working_set.require
     iter_entry_points = working_set.iter_entry_points

From d93d2e4ccc630ae41fbb1316f939719e8f5461a1 Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Tue, 7 May 2024 15:46:43 -0400
Subject: [PATCH 104/135] Removed `typing_extensions` from vendored
 dependencies

---
 newsfragments/4324.removal.rst                |    1 +
 .../INSTALLER                                 |    1 -
 .../typing_extensions-4.4.0.dist-info/LICENSE |  254 --
 .../METADATA                                  |  189 --
 .../typing_extensions-4.4.0.dist-info/RECORD  |    8 -
 .../REQUESTED                                 |    0
 .../typing_extensions-4.4.0.dist-info/WHEEL   |    4 -
 pkg_resources/_vendor/typing_extensions.py    | 2209 ----------------
 pkg_resources/_vendor/vendored.txt            |    2 -
 pkg_resources/extern/__init__.py              |    1 -
 .../INSTALLER                                 |    1 -
 .../typing_extensions-4.0.1.dist-info/LICENSE |  254 --
 .../METADATA                                  |   35 -
 .../typing_extensions-4.0.1.dist-info/RECORD  |    8 -
 .../REQUESTED                                 |    0
 .../typing_extensions-4.0.1.dist-info/WHEEL   |    4 -
 setuptools/_vendor/typing_extensions.py       | 2296 -----------------
 setuptools/_vendor/vendored.txt               |    2 -
 setuptools/extern/__init__.py                 |    1 -
 tools/vendored.py                             |    3 +-
 20 files changed, 2 insertions(+), 5271 deletions(-)
 create mode 100644 newsfragments/4324.removal.rst
 delete mode 100644 pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/INSTALLER
 delete mode 100644 pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/LICENSE
 delete mode 100644 pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/METADATA
 delete mode 100644 pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/RECORD
 delete mode 100644 pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/REQUESTED
 delete mode 100644 pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/WHEEL
 delete mode 100644 pkg_resources/_vendor/typing_extensions.py
 delete mode 100644 setuptools/_vendor/typing_extensions-4.0.1.dist-info/INSTALLER
 delete mode 100644 setuptools/_vendor/typing_extensions-4.0.1.dist-info/LICENSE
 delete mode 100644 setuptools/_vendor/typing_extensions-4.0.1.dist-info/METADATA
 delete mode 100644 setuptools/_vendor/typing_extensions-4.0.1.dist-info/RECORD
 delete mode 100644 setuptools/_vendor/typing_extensions-4.0.1.dist-info/REQUESTED
 delete mode 100644 setuptools/_vendor/typing_extensions-4.0.1.dist-info/WHEEL
 delete mode 100644 setuptools/_vendor/typing_extensions.py

diff --git a/newsfragments/4324.removal.rst b/newsfragments/4324.removal.rst
new file mode 100644
index 0000000000..bd5e1cb641
--- /dev/null
+++ b/newsfragments/4324.removal.rst
@@ -0,0 +1 @@
+Removed `typing_extensions` from vendored dependencies -- by :user:`Avasam`
diff --git a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/INSTALLER b/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/INSTALLER
deleted file mode 100644
index a1b589e38a..0000000000
--- a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/LICENSE b/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/LICENSE
deleted file mode 100644
index 1df6b3b8de..0000000000
--- a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/LICENSE
+++ /dev/null
@@ -1,254 +0,0 @@
-A. HISTORY OF THE SOFTWARE
-==========================
-
-Python was created in the early 1990s by Guido van Rossum at Stichting
-Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
-as a successor of a language called ABC.  Guido remains Python's
-principal author, although it includes many contributions from others.
-
-In 1995, Guido continued his work on Python at the Corporation for
-National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
-in Reston, Virginia where he released several versions of the
-software.
-
-In May 2000, Guido and the Python core development team moved to
-BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
-year, the PythonLabs team moved to Digital Creations, which became
-Zope Corporation.  In 2001, the Python Software Foundation (PSF, see
-https://www.python.org/psf/) was formed, a non-profit organization
-created specifically to own Python-related Intellectual Property.
-Zope Corporation was a sponsoring member of the PSF.
-
-All Python releases are Open Source (see http://www.opensource.org for
-the Open Source Definition).  Historically, most, but not all, Python
-releases have also been GPL-compatible; the table below summarizes
-the various releases.
-
-    Release         Derived     Year        Owner       GPL-
-                    from                                compatible? (1)
-
-    0.9.0 thru 1.2              1991-1995   CWI         yes
-    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
-    1.6             1.5.2       2000        CNRI        no
-    2.0             1.6         2000        BeOpen.com  no
-    1.6.1           1.6         2001        CNRI        yes (2)
-    2.1             2.0+1.6.1   2001        PSF         no
-    2.0.1           2.0+1.6.1   2001        PSF         yes
-    2.1.1           2.1+2.0.1   2001        PSF         yes
-    2.1.2           2.1.1       2002        PSF         yes
-    2.1.3           2.1.2       2002        PSF         yes
-    2.2 and above   2.1.1       2001-now    PSF         yes
-
-Footnotes:
-
-(1) GPL-compatible doesn't mean that we're distributing Python under
-    the GPL.  All Python licenses, unlike the GPL, let you distribute
-    a modified version without making your changes open source.  The
-    GPL-compatible licenses make it possible to combine Python with
-    other software that is released under the GPL; the others don't.
-
-(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
-    because its license has a choice of law clause.  According to
-    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
-    is "not incompatible" with the GPL.
-
-Thanks to the many outside volunteers who have worked under Guido's
-direction to make these releases possible.
-
-
-B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
-===============================================================
-
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
---------------------------------------------
-
-1. This LICENSE AGREEMENT is between the Python Software Foundation
-("PSF"), and the Individual or Organization ("Licensee") accessing and
-otherwise using this software ("Python") in source or binary form and
-its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, PSF hereby
-grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
-analyze, test, perform and/or display publicly, prepare derivative works,
-distribute, and otherwise use Python alone or in any derivative version,
-provided, however, that PSF's License Agreement and PSF's notice of copyright,
-i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation;
-All Rights Reserved" are retained in Python alone or in any derivative version
-prepared by Licensee.
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python.
-
-4. PSF is making Python available to Licensee on an "AS IS"
-basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. Nothing in this License Agreement shall be deemed to create any
-relationship of agency, partnership, or joint venture between PSF and
-Licensee.  This License Agreement does not grant permission to use PSF
-trademarks or trade name in a trademark sense to endorse or promote
-products or services of Licensee, or any third party.
-
-8. By copying, installing or otherwise using Python, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
--------------------------------------------
-
-BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
-
-1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
-office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
-Individual or Organization ("Licensee") accessing and otherwise using
-this software in source or binary form and its associated
-documentation ("the Software").
-
-2. Subject to the terms and conditions of this BeOpen Python License
-Agreement, BeOpen hereby grants Licensee a non-exclusive,
-royalty-free, world-wide license to reproduce, analyze, test, perform
-and/or display publicly, prepare derivative works, distribute, and
-otherwise use the Software alone or in any derivative version,
-provided, however, that the BeOpen Python License is retained in the
-Software, alone or in any derivative version prepared by Licensee.
-
-3. BeOpen is making the Software available to Licensee on an "AS IS"
-basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
-SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
-AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
-DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-5. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-6. This License Agreement shall be governed by and interpreted in all
-respects by the law of the State of California, excluding conflict of
-law provisions.  Nothing in this License Agreement shall be deemed to
-create any relationship of agency, partnership, or joint venture
-between BeOpen and Licensee.  This License Agreement does not grant
-permission to use BeOpen trademarks or trade names in a trademark
-sense to endorse or promote products or services of Licensee, or any
-third party.  As an exception, the "BeOpen Python" logos available at
-http://www.pythonlabs.com/logos.html may be used according to the
-permissions granted on that web page.
-
-7. By copying, installing or otherwise using the software, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
----------------------------------------
-
-1. This LICENSE AGREEMENT is between the Corporation for National
-Research Initiatives, having an office at 1895 Preston White Drive,
-Reston, VA 20191 ("CNRI"), and the Individual or Organization
-("Licensee") accessing and otherwise using Python 1.6.1 software in
-source or binary form and its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, CNRI
-hereby grants Licensee a nonexclusive, royalty-free, world-wide
-license to reproduce, analyze, test, perform and/or display publicly,
-prepare derivative works, distribute, and otherwise use Python 1.6.1
-alone or in any derivative version, provided, however, that CNRI's
-License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
-1995-2001 Corporation for National Research Initiatives; All Rights
-Reserved" are retained in Python 1.6.1 alone or in any derivative
-version prepared by Licensee.  Alternately, in lieu of CNRI's License
-Agreement, Licensee may substitute the following text (omitting the
-quotes): "Python 1.6.1 is made available subject to the terms and
-conditions in CNRI's License Agreement.  This Agreement together with
-Python 1.6.1 may be located on the internet using the following
-unique, persistent identifier (known as a handle): 1895.22/1013.  This
-Agreement may also be obtained from a proxy server on the internet
-using the following URL: http://hdl.handle.net/1895.22/1013".
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python 1.6.1 or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python 1.6.1.
-
-4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
-basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. This License Agreement shall be governed by the federal
-intellectual property law of the United States, including without
-limitation the federal copyright law, and, to the extent such
-U.S. federal law does not apply, by the law of the Commonwealth of
-Virginia, excluding Virginia's conflict of law provisions.
-Notwithstanding the foregoing, with regard to derivative works based
-on Python 1.6.1 that incorporate non-separable material that was
-previously distributed under the GNU General Public License (GPL), the
-law of the Commonwealth of Virginia shall govern this License
-Agreement only as to issues arising under or with respect to
-Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
-License Agreement shall be deemed to create any relationship of
-agency, partnership, or joint venture between CNRI and Licensee.  This
-License Agreement does not grant permission to use CNRI trademarks or
-trade name in a trademark sense to endorse or promote products or
-services of Licensee, or any third party.
-
-8. By clicking on the "ACCEPT" button where indicated, or by copying,
-installing or otherwise using Python 1.6.1, Licensee agrees to be
-bound by the terms and conditions of this License Agreement.
-
-        ACCEPT
-
-
-CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
---------------------------------------------------
-
-Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
-The Netherlands.  All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies and that
-both that copyright notice and this permission notice appear in
-supporting documentation, and that the name of Stichting Mathematisch
-Centrum or CWI not be used in advertising or publicity pertaining to
-distribution of the software without specific, written prior
-permission.
-
-STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
-THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
-FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/METADATA b/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/METADATA
deleted file mode 100644
index 1ed963a12c..0000000000
--- a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/METADATA
+++ /dev/null
@@ -1,189 +0,0 @@
-Metadata-Version: 2.1
-Name: typing_extensions
-Version: 4.4.0
-Summary: Backported and Experimental Type Hints for Python 3.7+
-Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing
-Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" 
-Requires-Python: >=3.7
-Description-Content-Type: text/markdown
-Classifier: Development Status :: 5 - Production/Stable
-Classifier: Environment :: Console
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Python Software Foundation License
-Classifier: Operating System :: OS Independent
-Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3 :: Only
-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: Topic :: Software Development
-Project-URL: Bug Tracker, https://github.com/python/typing_extensions/issues
-Project-URL: Changes, https://github.com/python/typing_extensions/blob/main/CHANGELOG.md
-Project-URL: Documentation, https://typing.readthedocs.io/
-Project-URL: Home, https://github.com/python/typing_extensions
-Project-URL: Q & A, https://github.com/python/typing/discussions
-Project-URL: Repository, https://github.com/python/typing_extensions
-
-# Typing Extensions
-
-[![Chat at https://gitter.im/python/typing](https://badges.gitter.im/python/typing.svg)](https://gitter.im/python/typing)
-
-## Overview
-
-The `typing_extensions` module serves two related purposes:
-
-- Enable use of new type system features on older Python versions. For example,
-  `typing.TypeGuard` is new in Python 3.10, but `typing_extensions` allows
-  users on previous Python versions to use it too.
-- Enable experimentation with new type system PEPs before they are accepted and
-  added to the `typing` module.
-
-New features may be added to `typing_extensions` as soon as they are specified
-in a PEP that has been added to the [python/peps](https://github.com/python/peps)
-repository. If the PEP is accepted, the feature will then be added to `typing`
-for the next CPython release. No typing PEP has been rejected so far, so we
-haven't yet figured out how to deal with that possibility.
-
-Starting with version 4.0.0, `typing_extensions` uses
-[Semantic Versioning](https://semver.org/). The
-major version is incremented for all backwards-incompatible changes.
-Therefore, it's safe to depend
-on `typing_extensions` like this: `typing_extensions >=x.y, <(x+1)`,
-where `x.y` is the first version that includes all features you need.
-
-`typing_extensions` supports Python versions 3.7 and higher. In the future,
-support for older Python versions will be dropped some time after that version
-reaches end of life.
-
-## Included items
-
-This module currently contains the following:
-
-- Experimental features
-
-  - `override` (see PEP 698)
-  - The `default=` argument to `TypeVar`, `ParamSpec`, and `TypeVarTuple` (see PEP 696)
-  - The `infer_variance=` argument to `TypeVar` (see PEP 695)
-
-- In `typing` since Python 3.11
-
-  - `assert_never`
-  - `assert_type`
-  - `clear_overloads`
-  - `@dataclass_transform()` (see PEP 681)
-  - `get_overloads`
-  - `LiteralString` (see PEP 675)
-  - `Never`
-  - `NotRequired` (see PEP 655)
-  - `reveal_type`
-  - `Required` (see PEP 655)
-  - `Self` (see PEP 673)
-  - `TypeVarTuple` (see PEP 646; the `typing_extensions` version supports the `default=` argument from PEP 696)
-  - `Unpack` (see PEP 646)
-
-- In `typing` since Python 3.10
-
-  - `Concatenate` (see PEP 612)
-  - `ParamSpec` (see PEP 612; the `typing_extensions` version supports the `default=` argument from PEP 696)
-  - `ParamSpecArgs` (see PEP 612)
-  - `ParamSpecKwargs` (see PEP 612)
-  - `TypeAlias` (see PEP 613)
-  - `TypeGuard` (see PEP 647)
-  - `is_typeddict`
-
-- In `typing` since Python 3.9
-
-  - `Annotated` (see PEP 593)
-
-- In `typing` since Python 3.8
-
-  - `final` (see PEP 591)
-  - `Final` (see PEP 591)
-  - `Literal` (see PEP 586)
-  - `Protocol` (see PEP 544)
-  - `runtime_checkable` (see PEP 544)
-  - `TypedDict` (see PEP 589)
-  - `get_origin` (`typing_extensions` provides this function only in Python 3.7+)
-  - `get_args` (`typing_extensions` provides this function only in Python 3.7+)
-
-- In `typing` since Python 3.7
-
-  - `OrderedDict`
-
-- In `typing` since Python 3.5 or 3.6 (see [the typing documentation](https://docs.python.org/3.10/library/typing.html) for details)
-
-  - `AsyncContextManager`
-  - `AsyncGenerator`
-  - `AsyncIterable`
-  - `AsyncIterator`
-  - `Awaitable`
-  - `ChainMap`
-  - `ClassVar` (see PEP 526)
-  - `ContextManager`
-  - `Coroutine`
-  - `Counter`
-  - `DefaultDict`
-  - `Deque`
-  - `NewType`
-  - `NoReturn`
-  - `overload`
-  - `Text`
-  - `Type`
-  - `TYPE_CHECKING`
-  - `get_type_hints`
-
-- The following have always been present in `typing`, but the `typing_extensions` versions provide
-  additional features:
-
-  - `Any` (supports inheritance since Python 3.11)
-  - `NamedTuple` (supports multiple inheritance with `Generic` since Python 3.11)
-  - `TypeVar` (see PEPs 695 and 696)
-
-# Other Notes and Limitations
-
-Certain objects were changed after they were added to `typing`, and
-`typing_extensions` provides a backport even on newer Python versions:
-
-- `TypedDict` does not store runtime information
-  about which (if any) keys are non-required in Python 3.8, and does not
-  honor the `total` keyword with old-style `TypedDict()` in Python
-  3.9.0 and 3.9.1. `TypedDict` also does not support multiple inheritance
-  with `typing.Generic` on Python <3.11.
-- `get_origin` and `get_args` lack support for `Annotated` in
-  Python 3.8 and lack support for `ParamSpecArgs` and `ParamSpecKwargs`
-  in 3.9.
-- `@final` was changed in Python 3.11 to set the `.__final__` attribute.
-- `@overload` was changed in Python 3.11 to make function overloads
-  introspectable at runtime. In order to access overloads with
-  `typing_extensions.get_overloads()`, you must use
-  `@typing_extensions.overload`.
-- `NamedTuple` was changed in Python 3.11 to allow for multiple inheritance
-  with `typing.Generic`.
-- Since Python 3.11, it has been possible to inherit from `Any` at
-  runtime. `typing_extensions.Any` also provides this capability.
-- `TypeVar` gains two additional parameters, `default=` and `infer_variance=`,
-  in the draft PEPs 695 and 696, which are being considered for inclusion
-  in Python 3.12.
-
-There are a few types whose interface was modified between different
-versions of typing. For example, `typing.Sequence` was modified to
-subclass `typing.Reversible` as of Python 3.5.3.
-
-These changes are _not_ backported to prevent subtle compatibility
-issues when mixing the differing implementations of modified classes.
-
-Certain types have incorrect runtime behavior due to limitations of older
-versions of the typing module:
-
-- `ParamSpec` and `Concatenate` will not work with `get_args` and
-  `get_origin`. Certain PEP 612 special cases in user-defined
-  `Generic`s are also not available.
-
-These types are only guaranteed to work for static type checking.
-
-## Running tests
-
-To run tests, navigate into the appropriate source directory and run
-`test_typing_extensions.py`.
-
diff --git a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/RECORD b/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/RECORD
deleted file mode 100644
index e1132566df..0000000000
--- a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/RECORD
+++ /dev/null
@@ -1,8 +0,0 @@
-__pycache__/typing_extensions.cpython-312.pyc,,
-typing_extensions-4.4.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-typing_extensions-4.4.0.dist-info/LICENSE,sha256=x6-2XnVXB7n7kEhziaF20-09ADHVExr95FwjcV_16JE,12787
-typing_extensions-4.4.0.dist-info/METADATA,sha256=1zSh1eMLnLkLMMC6aZSGRKx3eRnivEGDFWGSVD1zqhA,7249
-typing_extensions-4.4.0.dist-info/RECORD,,
-typing_extensions-4.4.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-typing_extensions-4.4.0.dist-info/WHEEL,sha256=4TfKIB_xu-04bc2iKz6_zFt-gEFEEDU_31HGhqzOCE8,81
-typing_extensions.py,sha256=ipqWiq5AHzrwczt6c26AP05Llh6a5_GaXRpOBqbogHA,80078
diff --git a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/REQUESTED b/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/REQUESTED
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/WHEEL b/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/WHEEL
deleted file mode 100644
index 668ba4d015..0000000000
--- a/pkg_resources/_vendor/typing_extensions-4.4.0.dist-info/WHEEL
+++ /dev/null
@@ -1,4 +0,0 @@
-Wheel-Version: 1.0
-Generator: flit 3.7.1
-Root-Is-Purelib: true
-Tag: py3-none-any
diff --git a/pkg_resources/_vendor/typing_extensions.py b/pkg_resources/_vendor/typing_extensions.py
deleted file mode 100644
index ef42417c20..0000000000
--- a/pkg_resources/_vendor/typing_extensions.py
+++ /dev/null
@@ -1,2209 +0,0 @@
-import abc
-import collections
-import collections.abc
-import functools
-import operator
-import sys
-import types as _types
-import typing
-
-
-__all__ = [
-    # Super-special typing primitives.
-    'Any',
-    'ClassVar',
-    'Concatenate',
-    'Final',
-    'LiteralString',
-    'ParamSpec',
-    'ParamSpecArgs',
-    'ParamSpecKwargs',
-    'Self',
-    'Type',
-    'TypeVar',
-    'TypeVarTuple',
-    'Unpack',
-
-    # ABCs (from collections.abc).
-    'Awaitable',
-    'AsyncIterator',
-    'AsyncIterable',
-    'Coroutine',
-    'AsyncGenerator',
-    'AsyncContextManager',
-    'ChainMap',
-
-    # Concrete collection types.
-    'ContextManager',
-    'Counter',
-    'Deque',
-    'DefaultDict',
-    'NamedTuple',
-    'OrderedDict',
-    'TypedDict',
-
-    # Structural checks, a.k.a. protocols.
-    'SupportsIndex',
-
-    # One-off things.
-    'Annotated',
-    'assert_never',
-    'assert_type',
-    'clear_overloads',
-    'dataclass_transform',
-    'get_overloads',
-    'final',
-    'get_args',
-    'get_origin',
-    'get_type_hints',
-    'IntVar',
-    'is_typeddict',
-    'Literal',
-    'NewType',
-    'overload',
-    'override',
-    'Protocol',
-    'reveal_type',
-    'runtime',
-    'runtime_checkable',
-    'Text',
-    'TypeAlias',
-    'TypeGuard',
-    'TYPE_CHECKING',
-    'Never',
-    'NoReturn',
-    'Required',
-    'NotRequired',
-]
-
-# for backward compatibility
-PEP_560 = True
-GenericMeta = type
-
-# The functions below are modified copies of typing internal helpers.
-# They are needed by _ProtocolMeta and they provide support for PEP 646.
-
-_marker = object()
-
-
-def _check_generic(cls, parameters, elen=_marker):
-    """Check correct count for parameters of a generic cls (internal helper).
-    This gives a nice error message in case of count mismatch.
-    """
-    if not elen:
-        raise TypeError(f"{cls} is not a generic class")
-    if elen is _marker:
-        if not hasattr(cls, "__parameters__") or not cls.__parameters__:
-            raise TypeError(f"{cls} is not a generic class")
-        elen = len(cls.__parameters__)
-    alen = len(parameters)
-    if alen != elen:
-        if hasattr(cls, "__parameters__"):
-            parameters = [p for p in cls.__parameters__ if not _is_unpack(p)]
-            num_tv_tuples = sum(isinstance(p, TypeVarTuple) for p in parameters)
-            if (num_tv_tuples > 0) and (alen >= elen - num_tv_tuples):
-                return
-        raise TypeError(f"Too {'many' if alen > elen else 'few'} parameters for {cls};"
-                        f" actual {alen}, expected {elen}")
-
-
-if sys.version_info >= (3, 10):
-    def _should_collect_from_parameters(t):
-        return isinstance(
-            t, (typing._GenericAlias, _types.GenericAlias, _types.UnionType)
-        )
-elif sys.version_info >= (3, 9):
-    def _should_collect_from_parameters(t):
-        return isinstance(t, (typing._GenericAlias, _types.GenericAlias))
-else:
-    def _should_collect_from_parameters(t):
-        return isinstance(t, typing._GenericAlias) and not t._special
-
-
-def _collect_type_vars(types, typevar_types=None):
-    """Collect all type variable contained in types in order of
-    first appearance (lexicographic order). For example::
-
-        _collect_type_vars((T, List[S, T])) == (T, S)
-    """
-    if typevar_types is None:
-        typevar_types = typing.TypeVar
-    tvars = []
-    for t in types:
-        if (
-            isinstance(t, typevar_types) and
-            t not in tvars and
-            not _is_unpack(t)
-        ):
-            tvars.append(t)
-        if _should_collect_from_parameters(t):
-            tvars.extend([t for t in t.__parameters__ if t not in tvars])
-    return tuple(tvars)
-
-
-NoReturn = typing.NoReturn
-
-# Some unconstrained type variables.  These are used by the container types.
-# (These are not for export.)
-T = typing.TypeVar('T')  # Any type.
-KT = typing.TypeVar('KT')  # Key type.
-VT = typing.TypeVar('VT')  # Value type.
-T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
-T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
-
-
-if sys.version_info >= (3, 11):
-    from typing import Any
-else:
-
-    class _AnyMeta(type):
-        def __instancecheck__(self, obj):
-            if self is Any:
-                raise TypeError("typing_extensions.Any cannot be used with isinstance()")
-            return super().__instancecheck__(obj)
-
-        def __repr__(self):
-            if self is Any:
-                return "typing_extensions.Any"
-            return super().__repr__()
-
-    class Any(metaclass=_AnyMeta):
-        """Special type indicating an unconstrained type.
-        - Any is compatible with every type.
-        - Any assumed to have all methods.
-        - All values assumed to be instances of Any.
-        Note that all the above statements are true from the point of view of
-        static type checkers. At runtime, Any should not be used with instance
-        checks.
-        """
-        def __new__(cls, *args, **kwargs):
-            if cls is Any:
-                raise TypeError("Any cannot be instantiated")
-            return super().__new__(cls, *args, **kwargs)
-
-
-ClassVar = typing.ClassVar
-
-# On older versions of typing there is an internal class named "Final".
-# 3.8+
-if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
-    Final = typing.Final
-# 3.7
-else:
-    class _FinalForm(typing._SpecialForm, _root=True):
-
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            item = typing._type_check(parameters,
-                                      f'{self._name} accepts only a single type.')
-            return typing._GenericAlias(self, (item,))
-
-    Final = _FinalForm('Final',
-                       doc="""A special typing construct to indicate that a name
-                       cannot be re-assigned or overridden in a subclass.
-                       For example:
-
-                           MAX_SIZE: Final = 9000
-                           MAX_SIZE += 1  # Error reported by type checker
-
-                           class Connection:
-                               TIMEOUT: Final[int] = 10
-                           class FastConnector(Connection):
-                               TIMEOUT = 1  # Error reported by type checker
-
-                       There is no runtime checking of these properties.""")
-
-if sys.version_info >= (3, 11):
-    final = typing.final
-else:
-    # @final exists in 3.8+, but we backport it for all versions
-    # before 3.11 to keep support for the __final__ attribute.
-    # See https://bugs.python.org/issue46342
-    def final(f):
-        """This decorator can be used to indicate to type checkers that
-        the decorated method cannot be overridden, and decorated class
-        cannot be subclassed. For example:
-
-            class Base:
-                @final
-                def done(self) -> None:
-                    ...
-            class Sub(Base):
-                def done(self) -> None:  # Error reported by type checker
-                    ...
-            @final
-            class Leaf:
-                ...
-            class Other(Leaf):  # Error reported by type checker
-                ...
-
-        There is no runtime checking of these properties. The decorator
-        sets the ``__final__`` attribute to ``True`` on the decorated object
-        to allow runtime introspection.
-        """
-        try:
-            f.__final__ = True
-        except (AttributeError, TypeError):
-            # Skip the attribute silently if it is not writable.
-            # AttributeError happens if the object has __slots__ or a
-            # read-only property, TypeError if it's a builtin class.
-            pass
-        return f
-
-
-def IntVar(name):
-    return typing.TypeVar(name)
-
-
-# 3.8+:
-if hasattr(typing, 'Literal'):
-    Literal = typing.Literal
-# 3.7:
-else:
-    class _LiteralForm(typing._SpecialForm, _root=True):
-
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            return typing._GenericAlias(self, parameters)
-
-    Literal = _LiteralForm('Literal',
-                           doc="""A type that can be used to indicate to type checkers
-                           that the corresponding value has a value literally equivalent
-                           to the provided parameter. For example:
-
-                               var: Literal[4] = 4
-
-                           The type checker understands that 'var' is literally equal to
-                           the value 4 and no other value.
-
-                           Literal[...] cannot be subclassed. There is no runtime
-                           checking verifying that the parameter is actually a value
-                           instead of a type.""")
-
-
-_overload_dummy = typing._overload_dummy  # noqa
-
-
-if hasattr(typing, "get_overloads"):  # 3.11+
-    overload = typing.overload
-    get_overloads = typing.get_overloads
-    clear_overloads = typing.clear_overloads
-else:
-    # {module: {qualname: {firstlineno: func}}}
-    _overload_registry = collections.defaultdict(
-        functools.partial(collections.defaultdict, dict)
-    )
-
-    def overload(func):
-        """Decorator for overloaded functions/methods.
-
-        In a stub file, place two or more stub definitions for the same
-        function in a row, each decorated with @overload.  For example:
-
-        @overload
-        def utf8(value: None) -> None: ...
-        @overload
-        def utf8(value: bytes) -> bytes: ...
-        @overload
-        def utf8(value: str) -> bytes: ...
-
-        In a non-stub file (i.e. a regular .py file), do the same but
-        follow it with an implementation.  The implementation should *not*
-        be decorated with @overload.  For example:
-
-        @overload
-        def utf8(value: None) -> None: ...
-        @overload
-        def utf8(value: bytes) -> bytes: ...
-        @overload
-        def utf8(value: str) -> bytes: ...
-        def utf8(value):
-            # implementation goes here
-
-        The overloads for a function can be retrieved at runtime using the
-        get_overloads() function.
-        """
-        # classmethod and staticmethod
-        f = getattr(func, "__func__", func)
-        try:
-            _overload_registry[f.__module__][f.__qualname__][
-                f.__code__.co_firstlineno
-            ] = func
-        except AttributeError:
-            # Not a normal function; ignore.
-            pass
-        return _overload_dummy
-
-    def get_overloads(func):
-        """Return all defined overloads for *func* as a sequence."""
-        # classmethod and staticmethod
-        f = getattr(func, "__func__", func)
-        if f.__module__ not in _overload_registry:
-            return []
-        mod_dict = _overload_registry[f.__module__]
-        if f.__qualname__ not in mod_dict:
-            return []
-        return list(mod_dict[f.__qualname__].values())
-
-    def clear_overloads():
-        """Clear all overloads in the registry."""
-        _overload_registry.clear()
-
-
-# This is not a real generic class.  Don't use outside annotations.
-Type = typing.Type
-
-# Various ABCs mimicking those in collections.abc.
-# A few are simply re-exported for completeness.
-
-
-Awaitable = typing.Awaitable
-Coroutine = typing.Coroutine
-AsyncIterable = typing.AsyncIterable
-AsyncIterator = typing.AsyncIterator
-Deque = typing.Deque
-ContextManager = typing.ContextManager
-AsyncContextManager = typing.AsyncContextManager
-DefaultDict = typing.DefaultDict
-
-# 3.7.2+
-if hasattr(typing, 'OrderedDict'):
-    OrderedDict = typing.OrderedDict
-# 3.7.0-3.7.2
-else:
-    OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
-
-Counter = typing.Counter
-ChainMap = typing.ChainMap
-AsyncGenerator = typing.AsyncGenerator
-NewType = typing.NewType
-Text = typing.Text
-TYPE_CHECKING = typing.TYPE_CHECKING
-
-
-_PROTO_WHITELIST = ['Callable', 'Awaitable',
-                    'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator',
-                    'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
-                    'ContextManager', 'AsyncContextManager']
-
-
-def _get_protocol_attrs(cls):
-    attrs = set()
-    for base in cls.__mro__[:-1]:  # without object
-        if base.__name__ in ('Protocol', 'Generic'):
-            continue
-        annotations = getattr(base, '__annotations__', {})
-        for attr in list(base.__dict__.keys()) + list(annotations.keys()):
-            if (not attr.startswith('_abc_') and attr not in (
-                    '__abstractmethods__', '__annotations__', '__weakref__',
-                    '_is_protocol', '_is_runtime_protocol', '__dict__',
-                    '__args__', '__slots__',
-                    '__next_in_mro__', '__parameters__', '__origin__',
-                    '__orig_bases__', '__extra__', '__tree_hash__',
-                    '__doc__', '__subclasshook__', '__init__', '__new__',
-                    '__module__', '_MutableMapping__marker', '_gorg')):
-                attrs.add(attr)
-    return attrs
-
-
-def _is_callable_members_only(cls):
-    return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
-
-
-def _maybe_adjust_parameters(cls):
-    """Helper function used in Protocol.__init_subclass__ and _TypedDictMeta.__new__.
-
-    The contents of this function are very similar
-    to logic found in typing.Generic.__init_subclass__
-    on the CPython main branch.
-    """
-    tvars = []
-    if '__orig_bases__' in cls.__dict__:
-        tvars = typing._collect_type_vars(cls.__orig_bases__)
-        # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
-        # If found, tvars must be a subset of it.
-        # If not found, tvars is it.
-        # Also check for and reject plain Generic,
-        # and reject multiple Generic[...] and/or Protocol[...].
-        gvars = None
-        for base in cls.__orig_bases__:
-            if (isinstance(base, typing._GenericAlias) and
-                    base.__origin__ in (typing.Generic, Protocol)):
-                # for error messages
-                the_base = base.__origin__.__name__
-                if gvars is not None:
-                    raise TypeError(
-                        "Cannot inherit from Generic[...]"
-                        " and/or Protocol[...] multiple types.")
-                gvars = base.__parameters__
-        if gvars is None:
-            gvars = tvars
-        else:
-            tvarset = set(tvars)
-            gvarset = set(gvars)
-            if not tvarset <= gvarset:
-                s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
-                s_args = ', '.join(str(g) for g in gvars)
-                raise TypeError(f"Some type variables ({s_vars}) are"
-                                f" not listed in {the_base}[{s_args}]")
-            tvars = gvars
-    cls.__parameters__ = tuple(tvars)
-
-
-# 3.8+
-if hasattr(typing, 'Protocol'):
-    Protocol = typing.Protocol
-# 3.7
-else:
-
-    def _no_init(self, *args, **kwargs):
-        if type(self)._is_protocol:
-            raise TypeError('Protocols cannot be instantiated')
-
-    class _ProtocolMeta(abc.ABCMeta):  # noqa: B024
-        # This metaclass is a bit unfortunate and exists only because of the lack
-        # of __instancehook__.
-        def __instancecheck__(cls, instance):
-            # We need this method for situations where attributes are
-            # assigned in __init__.
-            if ((not getattr(cls, '_is_protocol', False) or
-                 _is_callable_members_only(cls)) and
-                    issubclass(instance.__class__, cls)):
-                return True
-            if cls._is_protocol:
-                if all(hasattr(instance, attr) and
-                       (not callable(getattr(cls, attr, None)) or
-                        getattr(instance, attr) is not None)
-                       for attr in _get_protocol_attrs(cls)):
-                    return True
-            return super().__instancecheck__(instance)
-
-    class Protocol(metaclass=_ProtocolMeta):
-        # There is quite a lot of overlapping code with typing.Generic.
-        # Unfortunately it is hard to avoid this while these live in two different
-        # modules. The duplicated code will be removed when Protocol is moved to typing.
-        """Base class for protocol classes. Protocol classes are defined as::
-
-            class Proto(Protocol):
-                def meth(self) -> int:
-                    ...
-
-        Such classes are primarily used with static type checkers that recognize
-        structural subtyping (static duck-typing), for example::
-
-            class C:
-                def meth(self) -> int:
-                    return 0
-
-            def func(x: Proto) -> int:
-                return x.meth()
-
-            func(C())  # Passes static type check
-
-        See PEP 544 for details. Protocol classes decorated with
-        @typing_extensions.runtime act as simple-minded runtime protocol that checks
-        only the presence of given attributes, ignoring their type signatures.
-
-        Protocol classes can be generic, they are defined as::
-
-            class GenProto(Protocol[T]):
-                def meth(self) -> T:
-                    ...
-        """
-        __slots__ = ()
-        _is_protocol = True
-
-        def __new__(cls, *args, **kwds):
-            if cls is Protocol:
-                raise TypeError("Type Protocol cannot be instantiated; "
-                                "it can only be used as a base class")
-            return super().__new__(cls)
-
-        @typing._tp_cache
-        def __class_getitem__(cls, params):
-            if not isinstance(params, tuple):
-                params = (params,)
-            if not params and cls is not typing.Tuple:
-                raise TypeError(
-                    f"Parameter list to {cls.__qualname__}[...] cannot be empty")
-            msg = "Parameters to generic types must be types."
-            params = tuple(typing._type_check(p, msg) for p in params)  # noqa
-            if cls is Protocol:
-                # Generic can only be subscripted with unique type variables.
-                if not all(isinstance(p, typing.TypeVar) for p in params):
-                    i = 0
-                    while isinstance(params[i], typing.TypeVar):
-                        i += 1
-                    raise TypeError(
-                        "Parameters to Protocol[...] must all be type variables."
-                        f" Parameter {i + 1} is {params[i]}")
-                if len(set(params)) != len(params):
-                    raise TypeError(
-                        "Parameters to Protocol[...] must all be unique")
-            else:
-                # Subscripting a regular Generic subclass.
-                _check_generic(cls, params, len(cls.__parameters__))
-            return typing._GenericAlias(cls, params)
-
-        def __init_subclass__(cls, *args, **kwargs):
-            if '__orig_bases__' in cls.__dict__:
-                error = typing.Generic in cls.__orig_bases__
-            else:
-                error = typing.Generic in cls.__bases__
-            if error:
-                raise TypeError("Cannot inherit from plain Generic")
-            _maybe_adjust_parameters(cls)
-
-            # Determine if this is a protocol or a concrete subclass.
-            if not cls.__dict__.get('_is_protocol', None):
-                cls._is_protocol = any(b is Protocol for b in cls.__bases__)
-
-            # Set (or override) the protocol subclass hook.
-            def _proto_hook(other):
-                if not cls.__dict__.get('_is_protocol', None):
-                    return NotImplemented
-                if not getattr(cls, '_is_runtime_protocol', False):
-                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
-                        return NotImplemented
-                    raise TypeError("Instance and class checks can only be used with"
-                                    " @runtime protocols")
-                if not _is_callable_members_only(cls):
-                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
-                        return NotImplemented
-                    raise TypeError("Protocols with non-method members"
-                                    " don't support issubclass()")
-                if not isinstance(other, type):
-                    # Same error as for issubclass(1, int)
-                    raise TypeError('issubclass() arg 1 must be a class')
-                for attr in _get_protocol_attrs(cls):
-                    for base in other.__mro__:
-                        if attr in base.__dict__:
-                            if base.__dict__[attr] is None:
-                                return NotImplemented
-                            break
-                        annotations = getattr(base, '__annotations__', {})
-                        if (isinstance(annotations, typing.Mapping) and
-                                attr in annotations and
-                                isinstance(other, _ProtocolMeta) and
-                                other._is_protocol):
-                            break
-                    else:
-                        return NotImplemented
-                return True
-            if '__subclasshook__' not in cls.__dict__:
-                cls.__subclasshook__ = _proto_hook
-
-            # We have nothing more to do for non-protocols.
-            if not cls._is_protocol:
-                return
-
-            # Check consistency of bases.
-            for base in cls.__bases__:
-                if not (base in (object, typing.Generic) or
-                        base.__module__ == 'collections.abc' and
-                        base.__name__ in _PROTO_WHITELIST or
-                        isinstance(base, _ProtocolMeta) and base._is_protocol):
-                    raise TypeError('Protocols can only inherit from other'
-                                    f' protocols, got {repr(base)}')
-            cls.__init__ = _no_init
-
-
-# 3.8+
-if hasattr(typing, 'runtime_checkable'):
-    runtime_checkable = typing.runtime_checkable
-# 3.7
-else:
-    def runtime_checkable(cls):
-        """Mark a protocol class as a runtime protocol, so that it
-        can be used with isinstance() and issubclass(). Raise TypeError
-        if applied to a non-protocol class.
-
-        This allows a simple-minded structural check very similar to the
-        one-offs in collections.abc such as Hashable.
-        """
-        if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol:
-            raise TypeError('@runtime_checkable can be only applied to protocol classes,'
-                            f' got {cls!r}')
-        cls._is_runtime_protocol = True
-        return cls
-
-
-# Exists for backwards compatibility.
-runtime = runtime_checkable
-
-
-# 3.8+
-if hasattr(typing, 'SupportsIndex'):
-    SupportsIndex = typing.SupportsIndex
-# 3.7
-else:
-    @runtime_checkable
-    class SupportsIndex(Protocol):
-        __slots__ = ()
-
-        @abc.abstractmethod
-        def __index__(self) -> int:
-            pass
-
-
-if hasattr(typing, "Required"):
-    # The standard library TypedDict in Python 3.8 does not store runtime information
-    # about which (if any) keys are optional.  See https://bugs.python.org/issue38834
-    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
-    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
-    # The standard library TypedDict below Python 3.11 does not store runtime
-    # information about optional and required keys when using Required or NotRequired.
-    # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11.
-    TypedDict = typing.TypedDict
-    _TypedDictMeta = typing._TypedDictMeta
-    is_typeddict = typing.is_typeddict
-else:
-    def _check_fails(cls, other):
-        try:
-            if sys._getframe(1).f_globals['__name__'] not in ['abc',
-                                                              'functools',
-                                                              'typing']:
-                # Typed dicts are only for static structural subtyping.
-                raise TypeError('TypedDict does not support instance and class checks')
-        except (AttributeError, ValueError):
-            pass
-        return False
-
-    def _dict_new(*args, **kwargs):
-        if not args:
-            raise TypeError('TypedDict.__new__(): not enough arguments')
-        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
-        return dict(*args, **kwargs)
-
-    _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)'
-
-    def _typeddict_new(*args, total=True, **kwargs):
-        if not args:
-            raise TypeError('TypedDict.__new__(): not enough arguments')
-        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
-        if args:
-            typename, args = args[0], args[1:]  # allow the "_typename" keyword be passed
-        elif '_typename' in kwargs:
-            typename = kwargs.pop('_typename')
-            import warnings
-            warnings.warn("Passing '_typename' as keyword argument is deprecated",
-                          DeprecationWarning, stacklevel=2)
-        else:
-            raise TypeError("TypedDict.__new__() missing 1 required positional "
-                            "argument: '_typename'")
-        if args:
-            try:
-                fields, = args  # allow the "_fields" keyword be passed
-            except ValueError:
-                raise TypeError('TypedDict.__new__() takes from 2 to 3 '
-                                f'positional arguments but {len(args) + 2} '
-                                'were given')
-        elif '_fields' in kwargs and len(kwargs) == 1:
-            fields = kwargs.pop('_fields')
-            import warnings
-            warnings.warn("Passing '_fields' as keyword argument is deprecated",
-                          DeprecationWarning, stacklevel=2)
-        else:
-            fields = None
-
-        if fields is None:
-            fields = kwargs
-        elif kwargs:
-            raise TypeError("TypedDict takes either a dict or keyword arguments,"
-                            " but not both")
-
-        ns = {'__annotations__': dict(fields)}
-        try:
-            # Setting correct module is necessary to make typed dict classes pickleable.
-            ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
-        except (AttributeError, ValueError):
-            pass
-
-        return _TypedDictMeta(typename, (), ns, total=total)
-
-    _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
-                                         ' /, *, total=True, **kwargs)')
-
-    class _TypedDictMeta(type):
-        def __init__(cls, name, bases, ns, total=True):
-            super().__init__(name, bases, ns)
-
-        def __new__(cls, name, bases, ns, total=True):
-            # Create new typed dict class object.
-            # This method is called directly when TypedDict is subclassed,
-            # or via _typeddict_new when TypedDict is instantiated. This way
-            # TypedDict supports all three syntaxes described in its docstring.
-            # Subclasses and instances of TypedDict return actual dictionaries
-            # via _dict_new.
-            ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
-            # Don't insert typing.Generic into __bases__ here,
-            # or Generic.__init_subclass__ will raise TypeError
-            # in the super().__new__() call.
-            # Instead, monkey-patch __bases__ onto the class after it's been created.
-            tp_dict = super().__new__(cls, name, (dict,), ns)
-
-            if any(issubclass(base, typing.Generic) for base in bases):
-                tp_dict.__bases__ = (typing.Generic, dict)
-                _maybe_adjust_parameters(tp_dict)
-
-            annotations = {}
-            own_annotations = ns.get('__annotations__', {})
-            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
-            own_annotations = {
-                n: typing._type_check(tp, msg) for n, tp in own_annotations.items()
-            }
-            required_keys = set()
-            optional_keys = set()
-
-            for base in bases:
-                annotations.update(base.__dict__.get('__annotations__', {}))
-                required_keys.update(base.__dict__.get('__required_keys__', ()))
-                optional_keys.update(base.__dict__.get('__optional_keys__', ()))
-
-            annotations.update(own_annotations)
-            for annotation_key, annotation_type in own_annotations.items():
-                annotation_origin = get_origin(annotation_type)
-                if annotation_origin is Annotated:
-                    annotation_args = get_args(annotation_type)
-                    if annotation_args:
-                        annotation_type = annotation_args[0]
-                        annotation_origin = get_origin(annotation_type)
-
-                if annotation_origin is Required:
-                    required_keys.add(annotation_key)
-                elif annotation_origin is NotRequired:
-                    optional_keys.add(annotation_key)
-                elif total:
-                    required_keys.add(annotation_key)
-                else:
-                    optional_keys.add(annotation_key)
-
-            tp_dict.__annotations__ = annotations
-            tp_dict.__required_keys__ = frozenset(required_keys)
-            tp_dict.__optional_keys__ = frozenset(optional_keys)
-            if not hasattr(tp_dict, '__total__'):
-                tp_dict.__total__ = total
-            return tp_dict
-
-        __instancecheck__ = __subclasscheck__ = _check_fails
-
-    TypedDict = _TypedDictMeta('TypedDict', (dict,), {})
-    TypedDict.__module__ = __name__
-    TypedDict.__doc__ = \
-        """A simple typed name space. At runtime it is equivalent to a plain dict.
-
-        TypedDict creates a dictionary type that expects all of its
-        instances to have a certain set of keys, with each key
-        associated with a value of a consistent type. This expectation
-        is not checked at runtime but is only enforced by type checkers.
-        Usage::
-
-            class Point2D(TypedDict):
-                x: int
-                y: int
-                label: str
-
-            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
-            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
-
-            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
-
-        The type info can be accessed via the Point2D.__annotations__ dict, and
-        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
-        TypedDict supports two additional equivalent forms::
-
-            Point2D = TypedDict('Point2D', x=int, y=int, label=str)
-            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
-
-        The class syntax is only supported in Python 3.6+, while two other
-        syntax forms work for Python 2.7 and 3.2+
-        """
-
-    if hasattr(typing, "_TypedDictMeta"):
-        _TYPEDDICT_TYPES = (typing._TypedDictMeta, _TypedDictMeta)
-    else:
-        _TYPEDDICT_TYPES = (_TypedDictMeta,)
-
-    def is_typeddict(tp):
-        """Check if an annotation is a TypedDict class
-
-        For example::
-            class Film(TypedDict):
-                title: str
-                year: int
-
-            is_typeddict(Film)  # => True
-            is_typeddict(Union[list, str])  # => False
-        """
-        return isinstance(tp, tuple(_TYPEDDICT_TYPES))
-
-
-if hasattr(typing, "assert_type"):
-    assert_type = typing.assert_type
-
-else:
-    def assert_type(__val, __typ):
-        """Assert (to the type checker) that the value is of the given type.
-
-        When the type checker encounters a call to assert_type(), it
-        emits an error if the value is not of the specified type::
-
-            def greet(name: str) -> None:
-                assert_type(name, str)  # ok
-                assert_type(name, int)  # type checker error
-
-        At runtime this returns the first argument unchanged and otherwise
-        does nothing.
-        """
-        return __val
-
-
-if hasattr(typing, "Required"):
-    get_type_hints = typing.get_type_hints
-else:
-    import functools
-    import types
-
-    # replaces _strip_annotations()
-    def _strip_extras(t):
-        """Strips Annotated, Required and NotRequired from a given type."""
-        if isinstance(t, _AnnotatedAlias):
-            return _strip_extras(t.__origin__)
-        if hasattr(t, "__origin__") and t.__origin__ in (Required, NotRequired):
-            return _strip_extras(t.__args__[0])
-        if isinstance(t, typing._GenericAlias):
-            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
-            if stripped_args == t.__args__:
-                return t
-            return t.copy_with(stripped_args)
-        if hasattr(types, "GenericAlias") and isinstance(t, types.GenericAlias):
-            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
-            if stripped_args == t.__args__:
-                return t
-            return types.GenericAlias(t.__origin__, stripped_args)
-        if hasattr(types, "UnionType") and isinstance(t, types.UnionType):
-            stripped_args = tuple(_strip_extras(a) for a in t.__args__)
-            if stripped_args == t.__args__:
-                return t
-            return functools.reduce(operator.or_, stripped_args)
-
-        return t
-
-    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
-        """Return type hints for an object.
-
-        This is often the same as obj.__annotations__, but it handles
-        forward references encoded as string literals, adds Optional[t] if a
-        default value equal to None is set and recursively replaces all
-        'Annotated[T, ...]', 'Required[T]' or 'NotRequired[T]' with 'T'
-        (unless 'include_extras=True').
-
-        The argument may be a module, class, method, or function. The annotations
-        are returned as a dictionary. For classes, annotations include also
-        inherited members.
-
-        TypeError is raised if the argument is not of a type that can contain
-        annotations, and an empty dictionary is returned if no annotations are
-        present.
-
-        BEWARE -- the behavior of globalns and localns is counterintuitive
-        (unless you are familiar with how eval() and exec() work).  The
-        search order is locals first, then globals.
-
-        - If no dict arguments are passed, an attempt is made to use the
-          globals from obj (or the respective module's globals for classes),
-          and these are also used as the locals.  If the object does not appear
-          to have globals, an empty dictionary is used.
-
-        - If one dict argument is passed, it is used for both globals and
-          locals.
-
-        - If two dict arguments are passed, they specify globals and
-          locals, respectively.
-        """
-        if hasattr(typing, "Annotated"):
-            hint = typing.get_type_hints(
-                obj, globalns=globalns, localns=localns, include_extras=True
-            )
-        else:
-            hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
-        if include_extras:
-            return hint
-        return {k: _strip_extras(t) for k, t in hint.items()}
-
-
-# Python 3.9+ has PEP 593 (Annotated)
-if hasattr(typing, 'Annotated'):
-    Annotated = typing.Annotated
-    # Not exported and not a public API, but needed for get_origin() and get_args()
-    # to work.
-    _AnnotatedAlias = typing._AnnotatedAlias
-# 3.7-3.8
-else:
-    class _AnnotatedAlias(typing._GenericAlias, _root=True):
-        """Runtime representation of an annotated type.
-
-        At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
-        with extra annotations. The alias behaves like a normal typing alias,
-        instantiating is the same as instantiating the underlying type, binding
-        it to types is also the same.
-        """
-        def __init__(self, origin, metadata):
-            if isinstance(origin, _AnnotatedAlias):
-                metadata = origin.__metadata__ + metadata
-                origin = origin.__origin__
-            super().__init__(origin, origin)
-            self.__metadata__ = metadata
-
-        def copy_with(self, params):
-            assert len(params) == 1
-            new_type = params[0]
-            return _AnnotatedAlias(new_type, self.__metadata__)
-
-        def __repr__(self):
-            return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
-                    f"{', '.join(repr(a) for a in self.__metadata__)}]")
-
-        def __reduce__(self):
-            return operator.getitem, (
-                Annotated, (self.__origin__,) + self.__metadata__
-            )
-
-        def __eq__(self, other):
-            if not isinstance(other, _AnnotatedAlias):
-                return NotImplemented
-            if self.__origin__ != other.__origin__:
-                return False
-            return self.__metadata__ == other.__metadata__
-
-        def __hash__(self):
-            return hash((self.__origin__, self.__metadata__))
-
-    class Annotated:
-        """Add context specific metadata to a type.
-
-        Example: Annotated[int, runtime_check.Unsigned] indicates to the
-        hypothetical runtime_check module that this type is an unsigned int.
-        Every other consumer of this type can ignore this metadata and treat
-        this type as int.
-
-        The first argument to Annotated must be a valid type (and will be in
-        the __origin__ field), the remaining arguments are kept as a tuple in
-        the __extra__ field.
-
-        Details:
-
-        - It's an error to call `Annotated` with less than two arguments.
-        - Nested Annotated are flattened::
-
-            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
-
-        - Instantiating an annotated type is equivalent to instantiating the
-        underlying type::
-
-            Annotated[C, Ann1](5) == C(5)
-
-        - Annotated can be used as a generic type alias::
-
-            Optimized = Annotated[T, runtime.Optimize()]
-            Optimized[int] == Annotated[int, runtime.Optimize()]
-
-            OptimizedList = Annotated[List[T], runtime.Optimize()]
-            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
-        """
-
-        __slots__ = ()
-
-        def __new__(cls, *args, **kwargs):
-            raise TypeError("Type Annotated cannot be instantiated.")
-
-        @typing._tp_cache
-        def __class_getitem__(cls, params):
-            if not isinstance(params, tuple) or len(params) < 2:
-                raise TypeError("Annotated[...] should be used "
-                                "with at least two arguments (a type and an "
-                                "annotation).")
-            allowed_special_forms = (ClassVar, Final)
-            if get_origin(params[0]) in allowed_special_forms:
-                origin = params[0]
-            else:
-                msg = "Annotated[t, ...]: t must be a type."
-                origin = typing._type_check(params[0], msg)
-            metadata = tuple(params[1:])
-            return _AnnotatedAlias(origin, metadata)
-
-        def __init_subclass__(cls, *args, **kwargs):
-            raise TypeError(
-                f"Cannot subclass {cls.__module__}.Annotated"
-            )
-
-# Python 3.8 has get_origin() and get_args() but those implementations aren't
-# Annotated-aware, so we can't use those. Python 3.9's versions don't support
-# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
-if sys.version_info[:2] >= (3, 10):
-    get_origin = typing.get_origin
-    get_args = typing.get_args
-# 3.7-3.9
-else:
-    try:
-        # 3.9+
-        from typing import _BaseGenericAlias
-    except ImportError:
-        _BaseGenericAlias = typing._GenericAlias
-    try:
-        # 3.9+
-        from typing import GenericAlias as _typing_GenericAlias
-    except ImportError:
-        _typing_GenericAlias = typing._GenericAlias
-
-    def get_origin(tp):
-        """Get the unsubscripted version of a type.
-
-        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
-        and Annotated. Return None for unsupported types. Examples::
-
-            get_origin(Literal[42]) is Literal
-            get_origin(int) is None
-            get_origin(ClassVar[int]) is ClassVar
-            get_origin(Generic) is Generic
-            get_origin(Generic[T]) is Generic
-            get_origin(Union[T, int]) is Union
-            get_origin(List[Tuple[T, T]][int]) == list
-            get_origin(P.args) is P
-        """
-        if isinstance(tp, _AnnotatedAlias):
-            return Annotated
-        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias, _BaseGenericAlias,
-                           ParamSpecArgs, ParamSpecKwargs)):
-            return tp.__origin__
-        if tp is typing.Generic:
-            return typing.Generic
-        return None
-
-    def get_args(tp):
-        """Get type arguments with all substitutions performed.
-
-        For unions, basic simplifications used by Union constructor are performed.
-        Examples::
-            get_args(Dict[str, int]) == (str, int)
-            get_args(int) == ()
-            get_args(Union[int, Union[T, int], str][int]) == (int, str)
-            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
-            get_args(Callable[[], T][int]) == ([], int)
-        """
-        if isinstance(tp, _AnnotatedAlias):
-            return (tp.__origin__,) + tp.__metadata__
-        if isinstance(tp, (typing._GenericAlias, _typing_GenericAlias)):
-            if getattr(tp, "_special", False):
-                return ()
-            res = tp.__args__
-            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
-                res = (list(res[:-1]), res[-1])
-            return res
-        return ()
-
-
-# 3.10+
-if hasattr(typing, 'TypeAlias'):
-    TypeAlias = typing.TypeAlias
-# 3.9
-elif sys.version_info[:2] >= (3, 9):
-    class _TypeAliasForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    @_TypeAliasForm
-    def TypeAlias(self, parameters):
-        """Special marker indicating that an assignment should
-        be recognized as a proper type alias definition by type
-        checkers.
-
-        For example::
-
-            Predicate: TypeAlias = Callable[..., bool]
-
-        It's invalid when used anywhere except as in the example above.
-        """
-        raise TypeError(f"{self} is not subscriptable")
-# 3.7-3.8
-else:
-    class _TypeAliasForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    TypeAlias = _TypeAliasForm('TypeAlias',
-                               doc="""Special marker indicating that an assignment should
-                               be recognized as a proper type alias definition by type
-                               checkers.
-
-                               For example::
-
-                                   Predicate: TypeAlias = Callable[..., bool]
-
-                               It's invalid when used anywhere except as in the example
-                               above.""")
-
-
-class _DefaultMixin:
-    """Mixin for TypeVarLike defaults."""
-
-    __slots__ = ()
-
-    def __init__(self, default):
-        if isinstance(default, (tuple, list)):
-            self.__default__ = tuple((typing._type_check(d, "Default must be a type")
-                                      for d in default))
-        elif default:
-            self.__default__ = typing._type_check(default, "Default must be a type")
-        else:
-            self.__default__ = None
-
-
-# Add default and infer_variance parameters from PEP 696 and 695
-class TypeVar(typing.TypeVar, _DefaultMixin, _root=True):
-    """Type variable."""
-
-    __module__ = 'typing'
-
-    def __init__(self, name, *constraints, bound=None,
-                 covariant=False, contravariant=False,
-                 default=None, infer_variance=False):
-        super().__init__(name, *constraints, bound=bound, covariant=covariant,
-                         contravariant=contravariant)
-        _DefaultMixin.__init__(self, default)
-        self.__infer_variance__ = infer_variance
-
-        # for pickling:
-        try:
-            def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
-        except (AttributeError, ValueError):
-            def_mod = None
-        if def_mod != 'typing_extensions':
-            self.__module__ = def_mod
-
-
-# Python 3.10+ has PEP 612
-if hasattr(typing, 'ParamSpecArgs'):
-    ParamSpecArgs = typing.ParamSpecArgs
-    ParamSpecKwargs = typing.ParamSpecKwargs
-# 3.7-3.9
-else:
-    class _Immutable:
-        """Mixin to indicate that object should not be copied."""
-        __slots__ = ()
-
-        def __copy__(self):
-            return self
-
-        def __deepcopy__(self, memo):
-            return self
-
-    class ParamSpecArgs(_Immutable):
-        """The args for a ParamSpec object.
-
-        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
-
-        ParamSpecArgs objects have a reference back to their ParamSpec:
-
-        P.args.__origin__ is P
-
-        This type is meant for runtime introspection and has no special meaning to
-        static type checkers.
-        """
-        def __init__(self, origin):
-            self.__origin__ = origin
-
-        def __repr__(self):
-            return f"{self.__origin__.__name__}.args"
-
-        def __eq__(self, other):
-            if not isinstance(other, ParamSpecArgs):
-                return NotImplemented
-            return self.__origin__ == other.__origin__
-
-    class ParamSpecKwargs(_Immutable):
-        """The kwargs for a ParamSpec object.
-
-        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
-
-        ParamSpecKwargs objects have a reference back to their ParamSpec:
-
-        P.kwargs.__origin__ is P
-
-        This type is meant for runtime introspection and has no special meaning to
-        static type checkers.
-        """
-        def __init__(self, origin):
-            self.__origin__ = origin
-
-        def __repr__(self):
-            return f"{self.__origin__.__name__}.kwargs"
-
-        def __eq__(self, other):
-            if not isinstance(other, ParamSpecKwargs):
-                return NotImplemented
-            return self.__origin__ == other.__origin__
-
-# 3.10+
-if hasattr(typing, 'ParamSpec'):
-
-    # Add default Parameter - PEP 696
-    class ParamSpec(typing.ParamSpec, _DefaultMixin, _root=True):
-        """Parameter specification variable."""
-
-        __module__ = 'typing'
-
-        def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
-                     default=None):
-            super().__init__(name, bound=bound, covariant=covariant,
-                             contravariant=contravariant)
-            _DefaultMixin.__init__(self, default)
-
-            # for pickling:
-            try:
-                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
-            except (AttributeError, ValueError):
-                def_mod = None
-            if def_mod != 'typing_extensions':
-                self.__module__ = def_mod
-
-# 3.7-3.9
-else:
-
-    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
-    class ParamSpec(list, _DefaultMixin):
-        """Parameter specification variable.
-
-        Usage::
-
-           P = ParamSpec('P')
-
-        Parameter specification variables exist primarily for the benefit of static
-        type checkers.  They are used to forward the parameter types of one
-        callable to another callable, a pattern commonly found in higher order
-        functions and decorators.  They are only valid when used in ``Concatenate``,
-        or s the first argument to ``Callable``. In Python 3.10 and higher,
-        they are also supported in user-defined Generics at runtime.
-        See class Generic for more information on generic types.  An
-        example for annotating a decorator::
-
-           T = TypeVar('T')
-           P = ParamSpec('P')
-
-           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
-               '''A type-safe decorator to add logging to a function.'''
-               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
-                   logging.info(f'{f.__name__} was called')
-                   return f(*args, **kwargs)
-               return inner
-
-           @add_logging
-           def add_two(x: float, y: float) -> float:
-               '''Add two numbers together.'''
-               return x + y
-
-        Parameter specification variables defined with covariant=True or
-        contravariant=True can be used to declare covariant or contravariant
-        generic types.  These keyword arguments are valid, but their actual semantics
-        are yet to be decided.  See PEP 612 for details.
-
-        Parameter specification variables can be introspected. e.g.:
-
-           P.__name__ == 'T'
-           P.__bound__ == None
-           P.__covariant__ == False
-           P.__contravariant__ == False
-
-        Note that only parameter specification variables defined in global scope can
-        be pickled.
-        """
-
-        # Trick Generic __parameters__.
-        __class__ = typing.TypeVar
-
-        @property
-        def args(self):
-            return ParamSpecArgs(self)
-
-        @property
-        def kwargs(self):
-            return ParamSpecKwargs(self)
-
-        def __init__(self, name, *, bound=None, covariant=False, contravariant=False,
-                     default=None):
-            super().__init__([self])
-            self.__name__ = name
-            self.__covariant__ = bool(covariant)
-            self.__contravariant__ = bool(contravariant)
-            if bound:
-                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
-            else:
-                self.__bound__ = None
-            _DefaultMixin.__init__(self, default)
-
-            # for pickling:
-            try:
-                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
-            except (AttributeError, ValueError):
-                def_mod = None
-            if def_mod != 'typing_extensions':
-                self.__module__ = def_mod
-
-        def __repr__(self):
-            if self.__covariant__:
-                prefix = '+'
-            elif self.__contravariant__:
-                prefix = '-'
-            else:
-                prefix = '~'
-            return prefix + self.__name__
-
-        def __hash__(self):
-            return object.__hash__(self)
-
-        def __eq__(self, other):
-            return self is other
-
-        def __reduce__(self):
-            return self.__name__
-
-        # Hack to get typing._type_check to pass.
-        def __call__(self, *args, **kwargs):
-            pass
-
-
-# 3.7-3.9
-if not hasattr(typing, 'Concatenate'):
-    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
-    class _ConcatenateGenericAlias(list):
-
-        # Trick Generic into looking into this for __parameters__.
-        __class__ = typing._GenericAlias
-
-        # Flag in 3.8.
-        _special = False
-
-        def __init__(self, origin, args):
-            super().__init__(args)
-            self.__origin__ = origin
-            self.__args__ = args
-
-        def __repr__(self):
-            _type_repr = typing._type_repr
-            return (f'{_type_repr(self.__origin__)}'
-                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
-
-        def __hash__(self):
-            return hash((self.__origin__, self.__args__))
-
-        # Hack to get typing._type_check to pass in Generic.
-        def __call__(self, *args, **kwargs):
-            pass
-
-        @property
-        def __parameters__(self):
-            return tuple(
-                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
-            )
-
-
-# 3.7-3.9
-@typing._tp_cache
-def _concatenate_getitem(self, parameters):
-    if parameters == ():
-        raise TypeError("Cannot take a Concatenate of no types.")
-    if not isinstance(parameters, tuple):
-        parameters = (parameters,)
-    if not isinstance(parameters[-1], ParamSpec):
-        raise TypeError("The last parameter to Concatenate should be a "
-                        "ParamSpec variable.")
-    msg = "Concatenate[arg, ...]: each arg must be a type."
-    parameters = tuple(typing._type_check(p, msg) for p in parameters)
-    return _ConcatenateGenericAlias(self, parameters)
-
-
-# 3.10+
-if hasattr(typing, 'Concatenate'):
-    Concatenate = typing.Concatenate
-    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa
-# 3.9
-elif sys.version_info[:2] >= (3, 9):
-    @_TypeAliasForm
-    def Concatenate(self, parameters):
-        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
-        higher order function which adds, removes or transforms parameters of a
-        callable.
-
-        For example::
-
-           Callable[Concatenate[int, P], int]
-
-        See PEP 612 for detailed information.
-        """
-        return _concatenate_getitem(self, parameters)
-# 3.7-8
-else:
-    class _ConcatenateForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            return _concatenate_getitem(self, parameters)
-
-    Concatenate = _ConcatenateForm(
-        'Concatenate',
-        doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
-        higher order function which adds, removes or transforms parameters of a
-        callable.
-
-        For example::
-
-           Callable[Concatenate[int, P], int]
-
-        See PEP 612 for detailed information.
-        """)
-
-# 3.10+
-if hasattr(typing, 'TypeGuard'):
-    TypeGuard = typing.TypeGuard
-# 3.9
-elif sys.version_info[:2] >= (3, 9):
-    class _TypeGuardForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    @_TypeGuardForm
-    def TypeGuard(self, parameters):
-        """Special typing form used to annotate the return type of a user-defined
-        type guard function.  ``TypeGuard`` only accepts a single type argument.
-        At runtime, functions marked this way should return a boolean.
-
-        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
-        type checkers to determine a more precise type of an expression within a
-        program's code flow.  Usually type narrowing is done by analyzing
-        conditional code flow and applying the narrowing to a block of code.  The
-        conditional expression here is sometimes referred to as a "type guard".
-
-        Sometimes it would be convenient to use a user-defined boolean function
-        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
-        return type to alert static type checkers to this intention.
-
-        Using  ``-> TypeGuard`` tells the static type checker that for a given
-        function:
-
-        1. The return value is a boolean.
-        2. If the return value is ``True``, the type of its argument
-        is the type inside ``TypeGuard``.
-
-        For example::
-
-            def is_str(val: Union[str, float]):
-                # "isinstance" type guard
-                if isinstance(val, str):
-                    # Type of ``val`` is narrowed to ``str``
-                    ...
-                else:
-                    # Else, type of ``val`` is narrowed to ``float``.
-                    ...
-
-        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
-        form of ``TypeA`` (it can even be a wider form) and this may lead to
-        type-unsafe results.  The main reason is to allow for things like
-        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
-        a subtype of the former, since ``List`` is invariant.  The responsibility of
-        writing type-safe type guards is left to the user.
-
-        ``TypeGuard`` also works with type variables.  For more information, see
-        PEP 647 (User-Defined Type Guards).
-        """
-        item = typing._type_check(parameters, f'{self} accepts only a single type.')
-        return typing._GenericAlias(self, (item,))
-# 3.7-3.8
-else:
-    class _TypeGuardForm(typing._SpecialForm, _root=True):
-
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            item = typing._type_check(parameters,
-                                      f'{self._name} accepts only a single type')
-            return typing._GenericAlias(self, (item,))
-
-    TypeGuard = _TypeGuardForm(
-        'TypeGuard',
-        doc="""Special typing form used to annotate the return type of a user-defined
-        type guard function.  ``TypeGuard`` only accepts a single type argument.
-        At runtime, functions marked this way should return a boolean.
-
-        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
-        type checkers to determine a more precise type of an expression within a
-        program's code flow.  Usually type narrowing is done by analyzing
-        conditional code flow and applying the narrowing to a block of code.  The
-        conditional expression here is sometimes referred to as a "type guard".
-
-        Sometimes it would be convenient to use a user-defined boolean function
-        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
-        return type to alert static type checkers to this intention.
-
-        Using  ``-> TypeGuard`` tells the static type checker that for a given
-        function:
-
-        1. The return value is a boolean.
-        2. If the return value is ``True``, the type of its argument
-        is the type inside ``TypeGuard``.
-
-        For example::
-
-            def is_str(val: Union[str, float]):
-                # "isinstance" type guard
-                if isinstance(val, str):
-                    # Type of ``val`` is narrowed to ``str``
-                    ...
-                else:
-                    # Else, type of ``val`` is narrowed to ``float``.
-                    ...
-
-        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
-        form of ``TypeA`` (it can even be a wider form) and this may lead to
-        type-unsafe results.  The main reason is to allow for things like
-        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
-        a subtype of the former, since ``List`` is invariant.  The responsibility of
-        writing type-safe type guards is left to the user.
-
-        ``TypeGuard`` also works with type variables.  For more information, see
-        PEP 647 (User-Defined Type Guards).
-        """)
-
-
-# Vendored from cpython typing._SpecialFrom
-class _SpecialForm(typing._Final, _root=True):
-    __slots__ = ('_name', '__doc__', '_getitem')
-
-    def __init__(self, getitem):
-        self._getitem = getitem
-        self._name = getitem.__name__
-        self.__doc__ = getitem.__doc__
-
-    def __getattr__(self, item):
-        if item in {'__name__', '__qualname__'}:
-            return self._name
-
-        raise AttributeError(item)
-
-    def __mro_entries__(self, bases):
-        raise TypeError(f"Cannot subclass {self!r}")
-
-    def __repr__(self):
-        return f'typing_extensions.{self._name}'
-
-    def __reduce__(self):
-        return self._name
-
-    def __call__(self, *args, **kwds):
-        raise TypeError(f"Cannot instantiate {self!r}")
-
-    def __or__(self, other):
-        return typing.Union[self, other]
-
-    def __ror__(self, other):
-        return typing.Union[other, self]
-
-    def __instancecheck__(self, obj):
-        raise TypeError(f"{self} cannot be used with isinstance()")
-
-    def __subclasscheck__(self, cls):
-        raise TypeError(f"{self} cannot be used with issubclass()")
-
-    @typing._tp_cache
-    def __getitem__(self, parameters):
-        return self._getitem(self, parameters)
-
-
-if hasattr(typing, "LiteralString"):
-    LiteralString = typing.LiteralString
-else:
-    @_SpecialForm
-    def LiteralString(self, params):
-        """Represents an arbitrary literal string.
-
-        Example::
-
-          from typing_extensions import LiteralString
-
-          def query(sql: LiteralString) -> ...:
-              ...
-
-          query("SELECT * FROM table")  # ok
-          query(f"SELECT * FROM {input()}")  # not ok
-
-        See PEP 675 for details.
-
-        """
-        raise TypeError(f"{self} is not subscriptable")
-
-
-if hasattr(typing, "Self"):
-    Self = typing.Self
-else:
-    @_SpecialForm
-    def Self(self, params):
-        """Used to spell the type of "self" in classes.
-
-        Example::
-
-          from typing import Self
-
-          class ReturnsSelf:
-              def parse(self, data: bytes) -> Self:
-                  ...
-                  return self
-
-        """
-
-        raise TypeError(f"{self} is not subscriptable")
-
-
-if hasattr(typing, "Never"):
-    Never = typing.Never
-else:
-    @_SpecialForm
-    def Never(self, params):
-        """The bottom type, a type that has no members.
-
-        This can be used to define a function that should never be
-        called, or a function that never returns::
-
-            from typing_extensions import Never
-
-            def never_call_me(arg: Never) -> None:
-                pass
-
-            def int_or_str(arg: int | str) -> None:
-                never_call_me(arg)  # type checker error
-                match arg:
-                    case int():
-                        print("It's an int")
-                    case str():
-                        print("It's a str")
-                    case _:
-                        never_call_me(arg)  # ok, arg is of type Never
-
-        """
-
-        raise TypeError(f"{self} is not subscriptable")
-
-
-if hasattr(typing, 'Required'):
-    Required = typing.Required
-    NotRequired = typing.NotRequired
-elif sys.version_info[:2] >= (3, 9):
-    class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    @_ExtensionsSpecialForm
-    def Required(self, parameters):
-        """A special typing construct to mark a key of a total=False TypedDict
-        as required. For example:
-
-            class Movie(TypedDict, total=False):
-                title: Required[str]
-                year: int
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-
-        There is no runtime checking that a required key is actually provided
-        when instantiating a related TypedDict.
-        """
-        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
-        return typing._GenericAlias(self, (item,))
-
-    @_ExtensionsSpecialForm
-    def NotRequired(self, parameters):
-        """A special typing construct to mark a key of a TypedDict as
-        potentially missing. For example:
-
-            class Movie(TypedDict):
-                title: str
-                year: NotRequired[int]
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-        """
-        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
-        return typing._GenericAlias(self, (item,))
-
-else:
-    class _RequiredForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            item = typing._type_check(parameters,
-                                      f'{self._name} accepts only a single type.')
-            return typing._GenericAlias(self, (item,))
-
-    Required = _RequiredForm(
-        'Required',
-        doc="""A special typing construct to mark a key of a total=False TypedDict
-        as required. For example:
-
-            class Movie(TypedDict, total=False):
-                title: Required[str]
-                year: int
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-
-        There is no runtime checking that a required key is actually provided
-        when instantiating a related TypedDict.
-        """)
-    NotRequired = _RequiredForm(
-        'NotRequired',
-        doc="""A special typing construct to mark a key of a TypedDict as
-        potentially missing. For example:
-
-            class Movie(TypedDict):
-                title: str
-                year: NotRequired[int]
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-        """)
-
-
-if hasattr(typing, "Unpack"):  # 3.11+
-    Unpack = typing.Unpack
-elif sys.version_info[:2] >= (3, 9):
-    class _UnpackSpecialForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    class _UnpackAlias(typing._GenericAlias, _root=True):
-        __class__ = typing.TypeVar
-
-    @_UnpackSpecialForm
-    def Unpack(self, parameters):
-        """A special typing construct to unpack a variadic type. For example:
-
-            Shape = TypeVarTuple('Shape')
-            Batch = NewType('Batch', int)
-
-            def add_batch_axis(
-                x: Array[Unpack[Shape]]
-            ) -> Array[Batch, Unpack[Shape]]: ...
-
-        """
-        item = typing._type_check(parameters, f'{self._name} accepts only a single type.')
-        return _UnpackAlias(self, (item,))
-
-    def _is_unpack(obj):
-        return isinstance(obj, _UnpackAlias)
-
-else:
-    class _UnpackAlias(typing._GenericAlias, _root=True):
-        __class__ = typing.TypeVar
-
-    class _UnpackForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            item = typing._type_check(parameters,
-                                      f'{self._name} accepts only a single type.')
-            return _UnpackAlias(self, (item,))
-
-    Unpack = _UnpackForm(
-        'Unpack',
-        doc="""A special typing construct to unpack a variadic type. For example:
-
-            Shape = TypeVarTuple('Shape')
-            Batch = NewType('Batch', int)
-
-            def add_batch_axis(
-                x: Array[Unpack[Shape]]
-            ) -> Array[Batch, Unpack[Shape]]: ...
-
-        """)
-
-    def _is_unpack(obj):
-        return isinstance(obj, _UnpackAlias)
-
-
-if hasattr(typing, "TypeVarTuple"):  # 3.11+
-
-    # Add default Parameter - PEP 696
-    class TypeVarTuple(typing.TypeVarTuple, _DefaultMixin, _root=True):
-        """Type variable tuple."""
-
-        def __init__(self, name, *, default=None):
-            super().__init__(name)
-            _DefaultMixin.__init__(self, default)
-
-            # for pickling:
-            try:
-                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
-            except (AttributeError, ValueError):
-                def_mod = None
-            if def_mod != 'typing_extensions':
-                self.__module__ = def_mod
-
-else:
-    class TypeVarTuple(_DefaultMixin):
-        """Type variable tuple.
-
-        Usage::
-
-            Ts = TypeVarTuple('Ts')
-
-        In the same way that a normal type variable is a stand-in for a single
-        type such as ``int``, a type variable *tuple* is a stand-in for a *tuple*
-        type such as ``Tuple[int, str]``.
-
-        Type variable tuples can be used in ``Generic`` declarations.
-        Consider the following example::
-
-            class Array(Generic[*Ts]): ...
-
-        The ``Ts`` type variable tuple here behaves like ``tuple[T1, T2]``,
-        where ``T1`` and ``T2`` are type variables. To use these type variables
-        as type parameters of ``Array``, we must *unpack* the type variable tuple using
-        the star operator: ``*Ts``. The signature of ``Array`` then behaves
-        as if we had simply written ``class Array(Generic[T1, T2]): ...``.
-        In contrast to ``Generic[T1, T2]``, however, ``Generic[*Shape]`` allows
-        us to parameterise the class with an *arbitrary* number of type parameters.
-
-        Type variable tuples can be used anywhere a normal ``TypeVar`` can.
-        This includes class definitions, as shown above, as well as function
-        signatures and variable annotations::
-
-            class Array(Generic[*Ts]):
-
-                def __init__(self, shape: Tuple[*Ts]):
-                    self._shape: Tuple[*Ts] = shape
-
-                def get_shape(self) -> Tuple[*Ts]:
-                    return self._shape
-
-            shape = (Height(480), Width(640))
-            x: Array[Height, Width] = Array(shape)
-            y = abs(x)  # Inferred type is Array[Height, Width]
-            z = x + x   #        ...    is Array[Height, Width]
-            x.get_shape()  #     ...    is tuple[Height, Width]
-
-        """
-
-        # Trick Generic __parameters__.
-        __class__ = typing.TypeVar
-
-        def __iter__(self):
-            yield self.__unpacked__
-
-        def __init__(self, name, *, default=None):
-            self.__name__ = name
-            _DefaultMixin.__init__(self, default)
-
-            # for pickling:
-            try:
-                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
-            except (AttributeError, ValueError):
-                def_mod = None
-            if def_mod != 'typing_extensions':
-                self.__module__ = def_mod
-
-            self.__unpacked__ = Unpack[self]
-
-        def __repr__(self):
-            return self.__name__
-
-        def __hash__(self):
-            return object.__hash__(self)
-
-        def __eq__(self, other):
-            return self is other
-
-        def __reduce__(self):
-            return self.__name__
-
-        def __init_subclass__(self, *args, **kwds):
-            if '_root' not in kwds:
-                raise TypeError("Cannot subclass special typing classes")
-
-
-if hasattr(typing, "reveal_type"):
-    reveal_type = typing.reveal_type
-else:
-    def reveal_type(__obj: T) -> T:
-        """Reveal the inferred type of a variable.
-
-        When a static type checker encounters a call to ``reveal_type()``,
-        it will emit the inferred type of the argument::
-
-            x: int = 1
-            reveal_type(x)
-
-        Running a static type checker (e.g., ``mypy``) on this example
-        will produce output similar to 'Revealed type is "builtins.int"'.
-
-        At runtime, the function prints the runtime type of the
-        argument and returns it unchanged.
-
-        """
-        print(f"Runtime type is {type(__obj).__name__!r}", file=sys.stderr)
-        return __obj
-
-
-if hasattr(typing, "assert_never"):
-    assert_never = typing.assert_never
-else:
-    def assert_never(__arg: Never) -> Never:
-        """Assert to the type checker that a line of code is unreachable.
-
-        Example::
-
-            def int_or_str(arg: int | str) -> None:
-                match arg:
-                    case int():
-                        print("It's an int")
-                    case str():
-                        print("It's a str")
-                    case _:
-                        assert_never(arg)
-
-        If a type checker finds that a call to assert_never() is
-        reachable, it will emit an error.
-
-        At runtime, this throws an exception when called.
-
-        """
-        raise AssertionError("Expected code to be unreachable")
-
-
-if hasattr(typing, 'dataclass_transform'):
-    dataclass_transform = typing.dataclass_transform
-else:
-    def dataclass_transform(
-        *,
-        eq_default: bool = True,
-        order_default: bool = False,
-        kw_only_default: bool = False,
-        field_specifiers: typing.Tuple[
-            typing.Union[typing.Type[typing.Any], typing.Callable[..., typing.Any]],
-            ...
-        ] = (),
-        **kwargs: typing.Any,
-    ) -> typing.Callable[[T], T]:
-        """Decorator that marks a function, class, or metaclass as providing
-        dataclass-like behavior.
-
-        Example:
-
-            from typing_extensions import dataclass_transform
-
-            _T = TypeVar("_T")
-
-            # Used on a decorator function
-            @dataclass_transform()
-            def create_model(cls: type[_T]) -> type[_T]:
-                ...
-                return cls
-
-            @create_model
-            class CustomerModel:
-                id: int
-                name: str
-
-            # Used on a base class
-            @dataclass_transform()
-            class ModelBase: ...
-
-            class CustomerModel(ModelBase):
-                id: int
-                name: str
-
-            # Used on a metaclass
-            @dataclass_transform()
-            class ModelMeta(type): ...
-
-            class ModelBase(metaclass=ModelMeta): ...
-
-            class CustomerModel(ModelBase):
-                id: int
-                name: str
-
-        Each of the ``CustomerModel`` classes defined in this example will now
-        behave similarly to a dataclass created with the ``@dataclasses.dataclass``
-        decorator. For example, the type checker will synthesize an ``__init__``
-        method.
-
-        The arguments to this decorator can be used to customize this behavior:
-        - ``eq_default`` indicates whether the ``eq`` parameter is assumed to be
-          True or False if it is omitted by the caller.
-        - ``order_default`` indicates whether the ``order`` parameter is
-          assumed to be True or False if it is omitted by the caller.
-        - ``kw_only_default`` indicates whether the ``kw_only`` parameter is
-          assumed to be True or False if it is omitted by the caller.
-        - ``field_specifiers`` specifies a static list of supported classes
-          or functions that describe fields, similar to ``dataclasses.field()``.
-
-        At runtime, this decorator records its arguments in the
-        ``__dataclass_transform__`` attribute on the decorated object.
-
-        See PEP 681 for details.
-
-        """
-        def decorator(cls_or_fn):
-            cls_or_fn.__dataclass_transform__ = {
-                "eq_default": eq_default,
-                "order_default": order_default,
-                "kw_only_default": kw_only_default,
-                "field_specifiers": field_specifiers,
-                "kwargs": kwargs,
-            }
-            return cls_or_fn
-        return decorator
-
-
-if hasattr(typing, "override"):
-    override = typing.override
-else:
-    _F = typing.TypeVar("_F", bound=typing.Callable[..., typing.Any])
-
-    def override(__arg: _F) -> _F:
-        """Indicate that a method is intended to override a method in a base class.
-
-        Usage:
-
-            class Base:
-                def method(self) -> None: ...
-                    pass
-
-            class Child(Base):
-                @override
-                def method(self) -> None:
-                    super().method()
-
-        When this decorator is applied to a method, the type checker will
-        validate that it overrides a method with the same name on a base class.
-        This helps prevent bugs that may occur when a base class is changed
-        without an equivalent change to a child class.
-
-        See PEP 698 for details.
-
-        """
-        return __arg
-
-
-# We have to do some monkey patching to deal with the dual nature of
-# Unpack/TypeVarTuple:
-# - We want Unpack to be a kind of TypeVar so it gets accepted in
-#   Generic[Unpack[Ts]]
-# - We want it to *not* be treated as a TypeVar for the purposes of
-#   counting generic parameters, so that when we subscript a generic,
-#   the runtime doesn't try to substitute the Unpack with the subscripted type.
-if not hasattr(typing, "TypeVarTuple"):
-    typing._collect_type_vars = _collect_type_vars
-    typing._check_generic = _check_generic
-
-
-# Backport typing.NamedTuple as it exists in Python 3.11.
-# In 3.11, the ability to define generic `NamedTuple`s was supported.
-# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8.
-if sys.version_info >= (3, 11):
-    NamedTuple = typing.NamedTuple
-else:
-    def _caller():
-        try:
-            return sys._getframe(2).f_globals.get('__name__', '__main__')
-        except (AttributeError, ValueError):  # For platforms without _getframe()
-            return None
-
-    def _make_nmtuple(name, types, module, defaults=()):
-        fields = [n for n, t in types]
-        annotations = {n: typing._type_check(t, f"field {n} annotation must be a type")
-                       for n, t in types}
-        nm_tpl = collections.namedtuple(name, fields,
-                                        defaults=defaults, module=module)
-        nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = annotations
-        # The `_field_types` attribute was removed in 3.9;
-        # in earlier versions, it is the same as the `__annotations__` attribute
-        if sys.version_info < (3, 9):
-            nm_tpl._field_types = annotations
-        return nm_tpl
-
-    _prohibited_namedtuple_fields = typing._prohibited
-    _special_namedtuple_fields = frozenset({'__module__', '__name__', '__annotations__'})
-
-    class _NamedTupleMeta(type):
-        def __new__(cls, typename, bases, ns):
-            assert _NamedTuple in bases
-            for base in bases:
-                if base is not _NamedTuple and base is not typing.Generic:
-                    raise TypeError(
-                        'can only inherit from a NamedTuple type and Generic')
-            bases = tuple(tuple if base is _NamedTuple else base for base in bases)
-            types = ns.get('__annotations__', {})
-            default_names = []
-            for field_name in types:
-                if field_name in ns:
-                    default_names.append(field_name)
-                elif default_names:
-                    raise TypeError(f"Non-default namedtuple field {field_name} "
-                                    f"cannot follow default field"
-                                    f"{'s' if len(default_names) > 1 else ''} "
-                                    f"{', '.join(default_names)}")
-            nm_tpl = _make_nmtuple(
-                typename, types.items(),
-                defaults=[ns[n] for n in default_names],
-                module=ns['__module__']
-            )
-            nm_tpl.__bases__ = bases
-            if typing.Generic in bases:
-                class_getitem = typing.Generic.__class_getitem__.__func__
-                nm_tpl.__class_getitem__ = classmethod(class_getitem)
-            # update from user namespace without overriding special namedtuple attributes
-            for key in ns:
-                if key in _prohibited_namedtuple_fields:
-                    raise AttributeError("Cannot overwrite NamedTuple attribute " + key)
-                elif key not in _special_namedtuple_fields and key not in nm_tpl._fields:
-                    setattr(nm_tpl, key, ns[key])
-            if typing.Generic in bases:
-                nm_tpl.__init_subclass__()
-            return nm_tpl
-
-    def NamedTuple(__typename, __fields=None, **kwargs):
-        if __fields is None:
-            __fields = kwargs.items()
-        elif kwargs:
-            raise TypeError("Either list of fields or keywords"
-                            " can be provided to NamedTuple, not both")
-        return _make_nmtuple(__typename, __fields, module=_caller())
-
-    NamedTuple.__doc__ = typing.NamedTuple.__doc__
-    _NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {})
-
-    # On 3.8+, alter the signature so that it matches typing.NamedTuple.
-    # The signature of typing.NamedTuple on >=3.8 is invalid syntax in Python 3.7,
-    # so just leave the signature as it is on 3.7.
-    if sys.version_info >= (3, 8):
-        NamedTuple.__text_signature__ = '(typename, fields=None, /, **kwargs)'
-
-    def _namedtuple_mro_entries(bases):
-        assert NamedTuple in bases
-        return (_NamedTuple,)
-
-    NamedTuple.__mro_entries__ = _namedtuple_mro_entries
diff --git a/pkg_resources/_vendor/vendored.txt b/pkg_resources/_vendor/vendored.txt
index f8cbfd967e..0c8fdc3823 100644
--- a/pkg_resources/_vendor/vendored.txt
+++ b/pkg_resources/_vendor/vendored.txt
@@ -1,8 +1,6 @@
 packaging==24
 
 platformdirs==2.6.2
-# required for platformdirs on Python < 3.8
-typing_extensions==4.4.0
 
 jaraco.text==3.7.0
 # required for jaraco.text on older Pythons
diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py
index 7f80b04164..363bdf3f06 100644
--- a/pkg_resources/extern/__init__.py
+++ b/pkg_resources/extern/__init__.py
@@ -79,7 +79,6 @@ def install(self):
 names = (
     'packaging',
     'platformdirs',
-    'typing_extensions',
     'jaraco',
     'importlib_resources',
     'zipp',
diff --git a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/INSTALLER b/setuptools/_vendor/typing_extensions-4.0.1.dist-info/INSTALLER
deleted file mode 100644
index a1b589e38a..0000000000
--- a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/INSTALLER
+++ /dev/null
@@ -1 +0,0 @@
-pip
diff --git a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/LICENSE b/setuptools/_vendor/typing_extensions-4.0.1.dist-info/LICENSE
deleted file mode 100644
index 583f9f6e61..0000000000
--- a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/LICENSE
+++ /dev/null
@@ -1,254 +0,0 @@
-A. HISTORY OF THE SOFTWARE
-==========================
-
-Python was created in the early 1990s by Guido van Rossum at Stichting
-Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands
-as a successor of a language called ABC.  Guido remains Python's
-principal author, although it includes many contributions from others.
-
-In 1995, Guido continued his work on Python at the Corporation for
-National Research Initiatives (CNRI, see http://www.cnri.reston.va.us)
-in Reston, Virginia where he released several versions of the
-software.
-
-In May 2000, Guido and the Python core development team moved to
-BeOpen.com to form the BeOpen PythonLabs team.  In October of the same
-year, the PythonLabs team moved to Digital Creations (now Zope
-Corporation, see http://www.zope.com).  In 2001, the Python Software
-Foundation (PSF, see http://www.python.org/psf/) was formed, a
-non-profit organization created specifically to own Python-related
-Intellectual Property.  Zope Corporation is a sponsoring member of
-the PSF.
-
-All Python releases are Open Source (see http://www.opensource.org for
-the Open Source Definition).  Historically, most, but not all, Python
-releases have also been GPL-compatible; the table below summarizes
-the various releases.
-
-    Release         Derived     Year        Owner       GPL-
-                    from                                compatible? (1)
-
-    0.9.0 thru 1.2              1991-1995   CWI         yes
-    1.3 thru 1.5.2  1.2         1995-1999   CNRI        yes
-    1.6             1.5.2       2000        CNRI        no
-    2.0             1.6         2000        BeOpen.com  no
-    1.6.1           1.6         2001        CNRI        yes (2)
-    2.1             2.0+1.6.1   2001        PSF         no
-    2.0.1           2.0+1.6.1   2001        PSF         yes
-    2.1.1           2.1+2.0.1   2001        PSF         yes
-    2.1.2           2.1.1       2002        PSF         yes
-    2.1.3           2.1.2       2002        PSF         yes
-    2.2 and above   2.1.1       2001-now    PSF         yes
-
-Footnotes:
-
-(1) GPL-compatible doesn't mean that we're distributing Python under
-    the GPL.  All Python licenses, unlike the GPL, let you distribute
-    a modified version without making your changes open source.  The
-    GPL-compatible licenses make it possible to combine Python with
-    other software that is released under the GPL; the others don't.
-
-(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
-    because its license has a choice of law clause.  According to
-    CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
-    is "not incompatible" with the GPL.
-
-Thanks to the many outside volunteers who have worked under Guido's
-direction to make these releases possible.
-
-
-B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
-===============================================================
-
-PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
---------------------------------------------
-
-1. This LICENSE AGREEMENT is between the Python Software Foundation
-("PSF"), and the Individual or Organization ("Licensee") accessing and
-otherwise using this software ("Python") in source or binary form and
-its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, PSF hereby
-grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
-analyze, test, perform and/or display publicly, prepare derivative works,
-distribute, and otherwise use Python alone or in any derivative version,
-provided, however, that PSF's License Agreement and PSF's notice of copyright,
-i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-2011, 2012, 2013, 2014 Python Software Foundation; All Rights Reserved" are
-retained in Python alone or in any derivative version prepared by Licensee.
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python.
-
-4. PSF is making Python available to Licensee on an "AS IS"
-basis.  PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. Nothing in this License Agreement shall be deemed to create any
-relationship of agency, partnership, or joint venture between PSF and
-Licensee.  This License Agreement does not grant permission to use PSF
-trademarks or trade name in a trademark sense to endorse or promote
-products or services of Licensee, or any third party.
-
-8. By copying, installing or otherwise using Python, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
--------------------------------------------
-
-BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
-
-1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
-office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
-Individual or Organization ("Licensee") accessing and otherwise using
-this software in source or binary form and its associated
-documentation ("the Software").
-
-2. Subject to the terms and conditions of this BeOpen Python License
-Agreement, BeOpen hereby grants Licensee a non-exclusive,
-royalty-free, world-wide license to reproduce, analyze, test, perform
-and/or display publicly, prepare derivative works, distribute, and
-otherwise use the Software alone or in any derivative version,
-provided, however, that the BeOpen Python License is retained in the
-Software, alone or in any derivative version prepared by Licensee.
-
-3. BeOpen is making the Software available to Licensee on an "AS IS"
-basis.  BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
-SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
-AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
-DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-5. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-6. This License Agreement shall be governed by and interpreted in all
-respects by the law of the State of California, excluding conflict of
-law provisions.  Nothing in this License Agreement shall be deemed to
-create any relationship of agency, partnership, or joint venture
-between BeOpen and Licensee.  This License Agreement does not grant
-permission to use BeOpen trademarks or trade names in a trademark
-sense to endorse or promote products or services of Licensee, or any
-third party.  As an exception, the "BeOpen Python" logos available at
-http://www.pythonlabs.com/logos.html may be used according to the
-permissions granted on that web page.
-
-7. By copying, installing or otherwise using the software, Licensee
-agrees to be bound by the terms and conditions of this License
-Agreement.
-
-
-CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
----------------------------------------
-
-1. This LICENSE AGREEMENT is between the Corporation for National
-Research Initiatives, having an office at 1895 Preston White Drive,
-Reston, VA 20191 ("CNRI"), and the Individual or Organization
-("Licensee") accessing and otherwise using Python 1.6.1 software in
-source or binary form and its associated documentation.
-
-2. Subject to the terms and conditions of this License Agreement, CNRI
-hereby grants Licensee a nonexclusive, royalty-free, world-wide
-license to reproduce, analyze, test, perform and/or display publicly,
-prepare derivative works, distribute, and otherwise use Python 1.6.1
-alone or in any derivative version, provided, however, that CNRI's
-License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
-1995-2001 Corporation for National Research Initiatives; All Rights
-Reserved" are retained in Python 1.6.1 alone or in any derivative
-version prepared by Licensee.  Alternately, in lieu of CNRI's License
-Agreement, Licensee may substitute the following text (omitting the
-quotes): "Python 1.6.1 is made available subject to the terms and
-conditions in CNRI's License Agreement.  This Agreement together with
-Python 1.6.1 may be located on the Internet using the following
-unique, persistent identifier (known as a handle): 1895.22/1013.  This
-Agreement may also be obtained from a proxy server on the Internet
-using the following URL: http://hdl.handle.net/1895.22/1013".
-
-3. In the event Licensee prepares a derivative work that is based on
-or incorporates Python 1.6.1 or any part thereof, and wants to make
-the derivative work available to others as provided herein, then
-Licensee hereby agrees to include in any such work a brief summary of
-the changes made to Python 1.6.1.
-
-4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
-basis.  CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
-IMPLIED.  BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
-DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
-FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
-INFRINGE ANY THIRD PARTY RIGHTS.
-
-5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
-1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
-A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
-OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
-
-6. This License Agreement will automatically terminate upon a material
-breach of its terms and conditions.
-
-7. This License Agreement shall be governed by the federal
-intellectual property law of the United States, including without
-limitation the federal copyright law, and, to the extent such
-U.S. federal law does not apply, by the law of the Commonwealth of
-Virginia, excluding Virginia's conflict of law provisions.
-Notwithstanding the foregoing, with regard to derivative works based
-on Python 1.6.1 that incorporate non-separable material that was
-previously distributed under the GNU General Public License (GPL), the
-law of the Commonwealth of Virginia shall govern this License
-Agreement only as to issues arising under or with respect to
-Paragraphs 4, 5, and 7 of this License Agreement.  Nothing in this
-License Agreement shall be deemed to create any relationship of
-agency, partnership, or joint venture between CNRI and Licensee.  This
-License Agreement does not grant permission to use CNRI trademarks or
-trade name in a trademark sense to endorse or promote products or
-services of Licensee, or any third party.
-
-8. By clicking on the "ACCEPT" button where indicated, or by copying,
-installing or otherwise using Python 1.6.1, Licensee agrees to be
-bound by the terms and conditions of this License Agreement.
-
-        ACCEPT
-
-
-CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
---------------------------------------------------
-
-Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
-The Netherlands.  All rights reserved.
-
-Permission to use, copy, modify, and distribute this software and its
-documentation for any purpose and without fee is hereby granted,
-provided that the above copyright notice appear in all copies and that
-both that copyright notice and this permission notice appear in
-supporting documentation, and that the name of Stichting Mathematisch
-Centrum or CWI not be used in advertising or publicity pertaining to
-distribution of the software without specific, written prior
-permission.
-
-STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
-THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
-FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
-OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/METADATA b/setuptools/_vendor/typing_extensions-4.0.1.dist-info/METADATA
deleted file mode 100644
index fe10dfd02a..0000000000
--- a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/METADATA
+++ /dev/null
@@ -1,35 +0,0 @@
-Metadata-Version: 2.1
-Name: typing_extensions
-Version: 4.0.1
-Summary: Backported and Experimental Type Hints for Python 3.6+
-Keywords: annotations,backport,checker,checking,function,hinting,hints,type,typechecking,typehinting,typehints,typing
-Author-email: "Guido van Rossum, Jukka Lehtosalo, Łukasz Langa, Michael Lee" 
-Requires-Python: >=3.6
-Description-Content-Type: text/x-rst
-Classifier: Development Status :: 3 - Alpha
-Classifier: Environment :: Console
-Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: Python Software Foundation License
-Classifier: Operating System :: OS Independent
-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: Topic :: Software Development
-Project-URL: Home, https://github.com/python/typing/blob/master/typing_extensions/README.rst
-
-Typing Extensions -- Backported and Experimental Type Hints for Python
-
-The ``typing`` module was added to the standard library in Python 3.5, but
-many new features have been added to the module since then.
-This means users of older Python versions who are unable to upgrade will not be
-able to take advantage of new types added to the ``typing`` module, such as
-``typing.Protocol`` or ``typing.TypedDict``.
-
-The ``typing_extensions`` module contains backports of these changes.
-Experimental types that may eventually be added to the ``typing``
-module are also included in ``typing_extensions``.
-
diff --git a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/RECORD b/setuptools/_vendor/typing_extensions-4.0.1.dist-info/RECORD
deleted file mode 100644
index efc5f26cf3..0000000000
--- a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/RECORD
+++ /dev/null
@@ -1,8 +0,0 @@
-__pycache__/typing_extensions.cpython-312.pyc,,
-typing_extensions-4.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
-typing_extensions-4.0.1.dist-info/LICENSE,sha256=_xfOlOECAk3raHc-scx0ynbaTmWPNzUx8Kwi1oprsa0,12755
-typing_extensions-4.0.1.dist-info/METADATA,sha256=iZ_5HONZZBXtF4kroz-IPZYIl9M8IE1B00R82dWcBqE,1736
-typing_extensions-4.0.1.dist-info/RECORD,,
-typing_extensions-4.0.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
-typing_extensions-4.0.1.dist-info/WHEEL,sha256=LVOPL_YDMEiGvRLgDK1hLkfhFCnTcxcAYZJtpNFses0,81
-typing_extensions.py,sha256=1uqi_RSlI7gos4eJB_NEV3d5wQwzTUQHd3_jrkbTo8Q,87149
diff --git a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/REQUESTED b/setuptools/_vendor/typing_extensions-4.0.1.dist-info/REQUESTED
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/WHEEL b/setuptools/_vendor/typing_extensions-4.0.1.dist-info/WHEEL
deleted file mode 100644
index 884ceb565c..0000000000
--- a/setuptools/_vendor/typing_extensions-4.0.1.dist-info/WHEEL
+++ /dev/null
@@ -1,4 +0,0 @@
-Wheel-Version: 1.0
-Generator: flit 3.5.1
-Root-Is-Purelib: true
-Tag: py3-none-any
diff --git a/setuptools/_vendor/typing_extensions.py b/setuptools/_vendor/typing_extensions.py
deleted file mode 100644
index 9f1c7aa31e..0000000000
--- a/setuptools/_vendor/typing_extensions.py
+++ /dev/null
@@ -1,2296 +0,0 @@
-import abc
-import collections
-import collections.abc
-import operator
-import sys
-import typing
-
-# After PEP 560, internal typing API was substantially reworked.
-# This is especially important for Protocol class which uses internal APIs
-# quite extensively.
-PEP_560 = sys.version_info[:3] >= (3, 7, 0)
-
-if PEP_560:
-    GenericMeta = type
-else:
-    # 3.6
-    from typing import GenericMeta, _type_vars  # noqa
-
-# The two functions below are copies of typing internal helpers.
-# They are needed by _ProtocolMeta
-
-
-def _no_slots_copy(dct):
-    dict_copy = dict(dct)
-    if '__slots__' in dict_copy:
-        for slot in dict_copy['__slots__']:
-            dict_copy.pop(slot, None)
-    return dict_copy
-
-
-def _check_generic(cls, parameters):
-    if not cls.__parameters__:
-        raise TypeError(f"{cls} is not a generic class")
-    alen = len(parameters)
-    elen = len(cls.__parameters__)
-    if alen != elen:
-        raise TypeError(f"Too {'many' if alen > elen else 'few'} arguments for {cls};"
-                        f" actual {alen}, expected {elen}")
-
-
-# Please keep __all__ alphabetized within each category.
-__all__ = [
-    # Super-special typing primitives.
-    'ClassVar',
-    'Concatenate',
-    'Final',
-    'ParamSpec',
-    'Self',
-    'Type',
-
-    # ABCs (from collections.abc).
-    'Awaitable',
-    'AsyncIterator',
-    'AsyncIterable',
-    'Coroutine',
-    'AsyncGenerator',
-    'AsyncContextManager',
-    'ChainMap',
-
-    # Concrete collection types.
-    'ContextManager',
-    'Counter',
-    'Deque',
-    'DefaultDict',
-    'OrderedDict',
-    'TypedDict',
-
-    # Structural checks, a.k.a. protocols.
-    'SupportsIndex',
-
-    # One-off things.
-    'Annotated',
-    'final',
-    'IntVar',
-    'Literal',
-    'NewType',
-    'overload',
-    'Protocol',
-    'runtime',
-    'runtime_checkable',
-    'Text',
-    'TypeAlias',
-    'TypeGuard',
-    'TYPE_CHECKING',
-]
-
-if PEP_560:
-    __all__.extend(["get_args", "get_origin", "get_type_hints"])
-
-# 3.6.2+
-if hasattr(typing, 'NoReturn'):
-    NoReturn = typing.NoReturn
-# 3.6.0-3.6.1
-else:
-    class _NoReturn(typing._FinalTypingBase, _root=True):
-        """Special type indicating functions that never return.
-        Example::
-
-          from typing import NoReturn
-
-          def stop() -> NoReturn:
-              raise Exception('no way')
-
-        This type is invalid in other positions, e.g., ``List[NoReturn]``
-        will fail in static type checkers.
-        """
-        __slots__ = ()
-
-        def __instancecheck__(self, obj):
-            raise TypeError("NoReturn cannot be used with isinstance().")
-
-        def __subclasscheck__(self, cls):
-            raise TypeError("NoReturn cannot be used with issubclass().")
-
-    NoReturn = _NoReturn(_root=True)
-
-# Some unconstrained type variables.  These are used by the container types.
-# (These are not for export.)
-T = typing.TypeVar('T')  # Any type.
-KT = typing.TypeVar('KT')  # Key type.
-VT = typing.TypeVar('VT')  # Value type.
-T_co = typing.TypeVar('T_co', covariant=True)  # Any type covariant containers.
-T_contra = typing.TypeVar('T_contra', contravariant=True)  # Ditto contravariant.
-
-ClassVar = typing.ClassVar
-
-# On older versions of typing there is an internal class named "Final".
-# 3.8+
-if hasattr(typing, 'Final') and sys.version_info[:2] >= (3, 7):
-    Final = typing.Final
-# 3.7
-elif sys.version_info[:2] >= (3, 7):
-    class _FinalForm(typing._SpecialForm, _root=True):
-
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            item = typing._type_check(parameters,
-                                      f'{self._name} accepts only single type')
-            return typing._GenericAlias(self, (item,))
-
-    Final = _FinalForm('Final',
-                       doc="""A special typing construct to indicate that a name
-                       cannot be re-assigned or overridden in a subclass.
-                       For example:
-
-                           MAX_SIZE: Final = 9000
-                           MAX_SIZE += 1  # Error reported by type checker
-
-                           class Connection:
-                               TIMEOUT: Final[int] = 10
-                           class FastConnector(Connection):
-                               TIMEOUT = 1  # Error reported by type checker
-
-                       There is no runtime checking of these properties.""")
-# 3.6
-else:
-    class _Final(typing._FinalTypingBase, _root=True):
-        """A special typing construct to indicate that a name
-        cannot be re-assigned or overridden in a subclass.
-        For example:
-
-            MAX_SIZE: Final = 9000
-            MAX_SIZE += 1  # Error reported by type checker
-
-            class Connection:
-                TIMEOUT: Final[int] = 10
-            class FastConnector(Connection):
-                TIMEOUT = 1  # Error reported by type checker
-
-        There is no runtime checking of these properties.
-        """
-
-        __slots__ = ('__type__',)
-
-        def __init__(self, tp=None, **kwds):
-            self.__type__ = tp
-
-        def __getitem__(self, item):
-            cls = type(self)
-            if self.__type__ is None:
-                return cls(typing._type_check(item,
-                           f'{cls.__name__[1:]} accepts only single type.'),
-                           _root=True)
-            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
-
-        def _eval_type(self, globalns, localns):
-            new_tp = typing._eval_type(self.__type__, globalns, localns)
-            if new_tp == self.__type__:
-                return self
-            return type(self)(new_tp, _root=True)
-
-        def __repr__(self):
-            r = super().__repr__()
-            if self.__type__ is not None:
-                r += f'[{typing._type_repr(self.__type__)}]'
-            return r
-
-        def __hash__(self):
-            return hash((type(self).__name__, self.__type__))
-
-        def __eq__(self, other):
-            if not isinstance(other, _Final):
-                return NotImplemented
-            if self.__type__ is not None:
-                return self.__type__ == other.__type__
-            return self is other
-
-    Final = _Final(_root=True)
-
-
-# 3.8+
-if hasattr(typing, 'final'):
-    final = typing.final
-# 3.6-3.7
-else:
-    def final(f):
-        """This decorator can be used to indicate to type checkers that
-        the decorated method cannot be overridden, and decorated class
-        cannot be subclassed. For example:
-
-            class Base:
-                @final
-                def done(self) -> None:
-                    ...
-            class Sub(Base):
-                def done(self) -> None:  # Error reported by type checker
-                    ...
-            @final
-            class Leaf:
-                ...
-            class Other(Leaf):  # Error reported by type checker
-                ...
-
-        There is no runtime checking of these properties.
-        """
-        return f
-
-
-def IntVar(name):
-    return typing.TypeVar(name)
-
-
-# 3.8+:
-if hasattr(typing, 'Literal'):
-    Literal = typing.Literal
-# 3.7:
-elif sys.version_info[:2] >= (3, 7):
-    class _LiteralForm(typing._SpecialForm, _root=True):
-
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            return typing._GenericAlias(self, parameters)
-
-    Literal = _LiteralForm('Literal',
-                           doc="""A type that can be used to indicate to type checkers
-                           that the corresponding value has a value literally equivalent
-                           to the provided parameter. For example:
-
-                               var: Literal[4] = 4
-
-                           The type checker understands that 'var' is literally equal to
-                           the value 4 and no other value.
-
-                           Literal[...] cannot be subclassed. There is no runtime
-                           checking verifying that the parameter is actually a value
-                           instead of a type.""")
-# 3.6:
-else:
-    class _Literal(typing._FinalTypingBase, _root=True):
-        """A type that can be used to indicate to type checkers that the
-        corresponding value has a value literally equivalent to the
-        provided parameter. For example:
-
-            var: Literal[4] = 4
-
-        The type checker understands that 'var' is literally equal to the
-        value 4 and no other value.
-
-        Literal[...] cannot be subclassed. There is no runtime checking
-        verifying that the parameter is actually a value instead of a type.
-        """
-
-        __slots__ = ('__values__',)
-
-        def __init__(self, values=None, **kwds):
-            self.__values__ = values
-
-        def __getitem__(self, values):
-            cls = type(self)
-            if self.__values__ is None:
-                if not isinstance(values, tuple):
-                    values = (values,)
-                return cls(values, _root=True)
-            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
-
-        def _eval_type(self, globalns, localns):
-            return self
-
-        def __repr__(self):
-            r = super().__repr__()
-            if self.__values__ is not None:
-                r += f'[{", ".join(map(typing._type_repr, self.__values__))}]'
-            return r
-
-        def __hash__(self):
-            return hash((type(self).__name__, self.__values__))
-
-        def __eq__(self, other):
-            if not isinstance(other, _Literal):
-                return NotImplemented
-            if self.__values__ is not None:
-                return self.__values__ == other.__values__
-            return self is other
-
-    Literal = _Literal(_root=True)
-
-
-_overload_dummy = typing._overload_dummy  # noqa
-overload = typing.overload
-
-
-# This is not a real generic class.  Don't use outside annotations.
-Type = typing.Type
-
-# Various ABCs mimicking those in collections.abc.
-# A few are simply re-exported for completeness.
-
-
-class _ExtensionsGenericMeta(GenericMeta):
-    def __subclasscheck__(self, subclass):
-        """This mimics a more modern GenericMeta.__subclasscheck__() logic
-        (that does not have problems with recursion) to work around interactions
-        between collections, typing, and typing_extensions on older
-        versions of Python, see https://github.com/python/typing/issues/501.
-        """
-        if self.__origin__ is not None:
-            if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
-                raise TypeError("Parameterized generics cannot be used with class "
-                                "or instance checks")
-            return False
-        if not self.__extra__:
-            return super().__subclasscheck__(subclass)
-        res = self.__extra__.__subclasshook__(subclass)
-        if res is not NotImplemented:
-            return res
-        if self.__extra__ in subclass.__mro__:
-            return True
-        for scls in self.__extra__.__subclasses__():
-            if isinstance(scls, GenericMeta):
-                continue
-            if issubclass(subclass, scls):
-                return True
-        return False
-
-
-Awaitable = typing.Awaitable
-Coroutine = typing.Coroutine
-AsyncIterable = typing.AsyncIterable
-AsyncIterator = typing.AsyncIterator
-
-# 3.6.1+
-if hasattr(typing, 'Deque'):
-    Deque = typing.Deque
-# 3.6.0
-else:
-    class Deque(collections.deque, typing.MutableSequence[T],
-                metaclass=_ExtensionsGenericMeta,
-                extra=collections.deque):
-        __slots__ = ()
-
-        def __new__(cls, *args, **kwds):
-            if cls._gorg is Deque:
-                return collections.deque(*args, **kwds)
-            return typing._generic_new(collections.deque, cls, *args, **kwds)
-
-ContextManager = typing.ContextManager
-# 3.6.2+
-if hasattr(typing, 'AsyncContextManager'):
-    AsyncContextManager = typing.AsyncContextManager
-# 3.6.0-3.6.1
-else:
-    from _collections_abc import _check_methods as _check_methods_in_mro  # noqa
-
-    class AsyncContextManager(typing.Generic[T_co]):
-        __slots__ = ()
-
-        async def __aenter__(self):
-            return self
-
-        @abc.abstractmethod
-        async def __aexit__(self, exc_type, exc_value, traceback):
-            return None
-
-        @classmethod
-        def __subclasshook__(cls, C):
-            if cls is AsyncContextManager:
-                return _check_methods_in_mro(C, "__aenter__", "__aexit__")
-            return NotImplemented
-
-DefaultDict = typing.DefaultDict
-
-# 3.7.2+
-if hasattr(typing, 'OrderedDict'):
-    OrderedDict = typing.OrderedDict
-# 3.7.0-3.7.2
-elif (3, 7, 0) <= sys.version_info[:3] < (3, 7, 2):
-    OrderedDict = typing._alias(collections.OrderedDict, (KT, VT))
-# 3.6
-else:
-    class OrderedDict(collections.OrderedDict, typing.MutableMapping[KT, VT],
-                      metaclass=_ExtensionsGenericMeta,
-                      extra=collections.OrderedDict):
-
-        __slots__ = ()
-
-        def __new__(cls, *args, **kwds):
-            if cls._gorg is OrderedDict:
-                return collections.OrderedDict(*args, **kwds)
-            return typing._generic_new(collections.OrderedDict, cls, *args, **kwds)
-
-# 3.6.2+
-if hasattr(typing, 'Counter'):
-    Counter = typing.Counter
-# 3.6.0-3.6.1
-else:
-    class Counter(collections.Counter,
-                  typing.Dict[T, int],
-                  metaclass=_ExtensionsGenericMeta, extra=collections.Counter):
-
-        __slots__ = ()
-
-        def __new__(cls, *args, **kwds):
-            if cls._gorg is Counter:
-                return collections.Counter(*args, **kwds)
-            return typing._generic_new(collections.Counter, cls, *args, **kwds)
-
-# 3.6.1+
-if hasattr(typing, 'ChainMap'):
-    ChainMap = typing.ChainMap
-elif hasattr(collections, 'ChainMap'):
-    class ChainMap(collections.ChainMap, typing.MutableMapping[KT, VT],
-                   metaclass=_ExtensionsGenericMeta,
-                   extra=collections.ChainMap):
-
-        __slots__ = ()
-
-        def __new__(cls, *args, **kwds):
-            if cls._gorg is ChainMap:
-                return collections.ChainMap(*args, **kwds)
-            return typing._generic_new(collections.ChainMap, cls, *args, **kwds)
-
-# 3.6.1+
-if hasattr(typing, 'AsyncGenerator'):
-    AsyncGenerator = typing.AsyncGenerator
-# 3.6.0
-else:
-    class AsyncGenerator(AsyncIterator[T_co], typing.Generic[T_co, T_contra],
-                         metaclass=_ExtensionsGenericMeta,
-                         extra=collections.abc.AsyncGenerator):
-        __slots__ = ()
-
-NewType = typing.NewType
-Text = typing.Text
-TYPE_CHECKING = typing.TYPE_CHECKING
-
-
-def _gorg(cls):
-    """This function exists for compatibility with old typing versions."""
-    assert isinstance(cls, GenericMeta)
-    if hasattr(cls, '_gorg'):
-        return cls._gorg
-    while cls.__origin__ is not None:
-        cls = cls.__origin__
-    return cls
-
-
-_PROTO_WHITELIST = ['Callable', 'Awaitable',
-                    'Iterable', 'Iterator', 'AsyncIterable', 'AsyncIterator',
-                    'Hashable', 'Sized', 'Container', 'Collection', 'Reversible',
-                    'ContextManager', 'AsyncContextManager']
-
-
-def _get_protocol_attrs(cls):
-    attrs = set()
-    for base in cls.__mro__[:-1]:  # without object
-        if base.__name__ in ('Protocol', 'Generic'):
-            continue
-        annotations = getattr(base, '__annotations__', {})
-        for attr in list(base.__dict__.keys()) + list(annotations.keys()):
-            if (not attr.startswith('_abc_') and attr not in (
-                    '__abstractmethods__', '__annotations__', '__weakref__',
-                    '_is_protocol', '_is_runtime_protocol', '__dict__',
-                    '__args__', '__slots__',
-                    '__next_in_mro__', '__parameters__', '__origin__',
-                    '__orig_bases__', '__extra__', '__tree_hash__',
-                    '__doc__', '__subclasshook__', '__init__', '__new__',
-                    '__module__', '_MutableMapping__marker', '_gorg')):
-                attrs.add(attr)
-    return attrs
-
-
-def _is_callable_members_only(cls):
-    return all(callable(getattr(cls, attr, None)) for attr in _get_protocol_attrs(cls))
-
-
-# 3.8+
-if hasattr(typing, 'Protocol'):
-    Protocol = typing.Protocol
-# 3.7
-elif PEP_560:
-    from typing import _collect_type_vars  # noqa
-
-    def _no_init(self, *args, **kwargs):
-        if type(self)._is_protocol:
-            raise TypeError('Protocols cannot be instantiated')
-
-    class _ProtocolMeta(abc.ABCMeta):
-        # This metaclass is a bit unfortunate and exists only because of the lack
-        # of __instancehook__.
-        def __instancecheck__(cls, instance):
-            # We need this method for situations where attributes are
-            # assigned in __init__.
-            if ((not getattr(cls, '_is_protocol', False) or
-                 _is_callable_members_only(cls)) and
-                    issubclass(instance.__class__, cls)):
-                return True
-            if cls._is_protocol:
-                if all(hasattr(instance, attr) and
-                       (not callable(getattr(cls, attr, None)) or
-                        getattr(instance, attr) is not None)
-                       for attr in _get_protocol_attrs(cls)):
-                    return True
-            return super().__instancecheck__(instance)
-
-    class Protocol(metaclass=_ProtocolMeta):
-        # There is quite a lot of overlapping code with typing.Generic.
-        # Unfortunately it is hard to avoid this while these live in two different
-        # modules. The duplicated code will be removed when Protocol is moved to typing.
-        """Base class for protocol classes. Protocol classes are defined as::
-
-            class Proto(Protocol):
-                def meth(self) -> int:
-                    ...
-
-        Such classes are primarily used with static type checkers that recognize
-        structural subtyping (static duck-typing), for example::
-
-            class C:
-                def meth(self) -> int:
-                    return 0
-
-            def func(x: Proto) -> int:
-                return x.meth()
-
-            func(C())  # Passes static type check
-
-        See PEP 544 for details. Protocol classes decorated with
-        @typing_extensions.runtime act as simple-minded runtime protocol that checks
-        only the presence of given attributes, ignoring their type signatures.
-
-        Protocol classes can be generic, they are defined as::
-
-            class GenProto(Protocol[T]):
-                def meth(self) -> T:
-                    ...
-        """
-        __slots__ = ()
-        _is_protocol = True
-
-        def __new__(cls, *args, **kwds):
-            if cls is Protocol:
-                raise TypeError("Type Protocol cannot be instantiated; "
-                                "it can only be used as a base class")
-            return super().__new__(cls)
-
-        @typing._tp_cache
-        def __class_getitem__(cls, params):
-            if not isinstance(params, tuple):
-                params = (params,)
-            if not params and cls is not typing.Tuple:
-                raise TypeError(
-                    f"Parameter list to {cls.__qualname__}[...] cannot be empty")
-            msg = "Parameters to generic types must be types."
-            params = tuple(typing._type_check(p, msg) for p in params)  # noqa
-            if cls is Protocol:
-                # Generic can only be subscripted with unique type variables.
-                if not all(isinstance(p, typing.TypeVar) for p in params):
-                    i = 0
-                    while isinstance(params[i], typing.TypeVar):
-                        i += 1
-                    raise TypeError(
-                        "Parameters to Protocol[...] must all be type variables."
-                        f" Parameter {i + 1} is {params[i]}")
-                if len(set(params)) != len(params):
-                    raise TypeError(
-                        "Parameters to Protocol[...] must all be unique")
-            else:
-                # Subscripting a regular Generic subclass.
-                _check_generic(cls, params)
-            return typing._GenericAlias(cls, params)
-
-        def __init_subclass__(cls, *args, **kwargs):
-            tvars = []
-            if '__orig_bases__' in cls.__dict__:
-                error = typing.Generic in cls.__orig_bases__
-            else:
-                error = typing.Generic in cls.__bases__
-            if error:
-                raise TypeError("Cannot inherit from plain Generic")
-            if '__orig_bases__' in cls.__dict__:
-                tvars = _collect_type_vars(cls.__orig_bases__)
-                # Look for Generic[T1, ..., Tn] or Protocol[T1, ..., Tn].
-                # If found, tvars must be a subset of it.
-                # If not found, tvars is it.
-                # Also check for and reject plain Generic,
-                # and reject multiple Generic[...] and/or Protocol[...].
-                gvars = None
-                for base in cls.__orig_bases__:
-                    if (isinstance(base, typing._GenericAlias) and
-                            base.__origin__ in (typing.Generic, Protocol)):
-                        # for error messages
-                        the_base = base.__origin__.__name__
-                        if gvars is not None:
-                            raise TypeError(
-                                "Cannot inherit from Generic[...]"
-                                " and/or Protocol[...] multiple types.")
-                        gvars = base.__parameters__
-                if gvars is None:
-                    gvars = tvars
-                else:
-                    tvarset = set(tvars)
-                    gvarset = set(gvars)
-                    if not tvarset <= gvarset:
-                        s_vars = ', '.join(str(t) for t in tvars if t not in gvarset)
-                        s_args = ', '.join(str(g) for g in gvars)
-                        raise TypeError(f"Some type variables ({s_vars}) are"
-                                        f" not listed in {the_base}[{s_args}]")
-                    tvars = gvars
-            cls.__parameters__ = tuple(tvars)
-
-            # Determine if this is a protocol or a concrete subclass.
-            if not cls.__dict__.get('_is_protocol', None):
-                cls._is_protocol = any(b is Protocol for b in cls.__bases__)
-
-            # Set (or override) the protocol subclass hook.
-            def _proto_hook(other):
-                if not cls.__dict__.get('_is_protocol', None):
-                    return NotImplemented
-                if not getattr(cls, '_is_runtime_protocol', False):
-                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
-                        return NotImplemented
-                    raise TypeError("Instance and class checks can only be used with"
-                                    " @runtime protocols")
-                if not _is_callable_members_only(cls):
-                    if sys._getframe(2).f_globals['__name__'] in ['abc', 'functools']:
-                        return NotImplemented
-                    raise TypeError("Protocols with non-method members"
-                                    " don't support issubclass()")
-                if not isinstance(other, type):
-                    # Same error as for issubclass(1, int)
-                    raise TypeError('issubclass() arg 1 must be a class')
-                for attr in _get_protocol_attrs(cls):
-                    for base in other.__mro__:
-                        if attr in base.__dict__:
-                            if base.__dict__[attr] is None:
-                                return NotImplemented
-                            break
-                        annotations = getattr(base, '__annotations__', {})
-                        if (isinstance(annotations, typing.Mapping) and
-                                attr in annotations and
-                                isinstance(other, _ProtocolMeta) and
-                                other._is_protocol):
-                            break
-                    else:
-                        return NotImplemented
-                return True
-            if '__subclasshook__' not in cls.__dict__:
-                cls.__subclasshook__ = _proto_hook
-
-            # We have nothing more to do for non-protocols.
-            if not cls._is_protocol:
-                return
-
-            # Check consistency of bases.
-            for base in cls.__bases__:
-                if not (base in (object, typing.Generic) or
-                        base.__module__ == 'collections.abc' and
-                        base.__name__ in _PROTO_WHITELIST or
-                        isinstance(base, _ProtocolMeta) and base._is_protocol):
-                    raise TypeError('Protocols can only inherit from other'
-                                    f' protocols, got {repr(base)}')
-            cls.__init__ = _no_init
-# 3.6
-else:
-    from typing import _next_in_mro, _type_check  # noqa
-
-    def _no_init(self, *args, **kwargs):
-        if type(self)._is_protocol:
-            raise TypeError('Protocols cannot be instantiated')
-
-    class _ProtocolMeta(GenericMeta):
-        """Internal metaclass for Protocol.
-
-        This exists so Protocol classes can be generic without deriving
-        from Generic.
-        """
-        def __new__(cls, name, bases, namespace,
-                    tvars=None, args=None, origin=None, extra=None, orig_bases=None):
-            # This is just a version copied from GenericMeta.__new__ that
-            # includes "Protocol" special treatment. (Comments removed for brevity.)
-            assert extra is None  # Protocols should not have extra
-            if tvars is not None:
-                assert origin is not None
-                assert all(isinstance(t, typing.TypeVar) for t in tvars), tvars
-            else:
-                tvars = _type_vars(bases)
-                gvars = None
-                for base in bases:
-                    if base is typing.Generic:
-                        raise TypeError("Cannot inherit from plain Generic")
-                    if (isinstance(base, GenericMeta) and
-                            base.__origin__ in (typing.Generic, Protocol)):
-                        if gvars is not None:
-                            raise TypeError(
-                                "Cannot inherit from Generic[...] or"
-                                " Protocol[...] multiple times.")
-                        gvars = base.__parameters__
-                if gvars is None:
-                    gvars = tvars
-                else:
-                    tvarset = set(tvars)
-                    gvarset = set(gvars)
-                    if not tvarset <= gvarset:
-                        s_vars = ", ".join(str(t) for t in tvars if t not in gvarset)
-                        s_args = ", ".join(str(g) for g in gvars)
-                        cls_name = "Generic" if any(b.__origin__ is typing.Generic
-                                                    for b in bases) else "Protocol"
-                        raise TypeError(f"Some type variables ({s_vars}) are"
-                                        f" not listed in {cls_name}[{s_args}]")
-                    tvars = gvars
-
-            initial_bases = bases
-            if (extra is not None and type(extra) is abc.ABCMeta and
-                    extra not in bases):
-                bases = (extra,) + bases
-            bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b
-                          for b in bases)
-            if any(isinstance(b, GenericMeta) and b is not typing.Generic for b in bases):
-                bases = tuple(b for b in bases if b is not typing.Generic)
-            namespace.update({'__origin__': origin, '__extra__': extra})
-            self = super(GenericMeta, cls).__new__(cls, name, bases, namespace,
-                                                   _root=True)
-            super(GenericMeta, self).__setattr__('_gorg',
-                                                 self if not origin else
-                                                 _gorg(origin))
-            self.__parameters__ = tvars
-            self.__args__ = tuple(... if a is typing._TypingEllipsis else
-                                  () if a is typing._TypingEmpty else
-                                  a for a in args) if args else None
-            self.__next_in_mro__ = _next_in_mro(self)
-            if orig_bases is None:
-                self.__orig_bases__ = initial_bases
-            elif origin is not None:
-                self._abc_registry = origin._abc_registry
-                self._abc_cache = origin._abc_cache
-            if hasattr(self, '_subs_tree'):
-                self.__tree_hash__ = (hash(self._subs_tree()) if origin else
-                                      super(GenericMeta, self).__hash__())
-            return self
-
-        def __init__(cls, *args, **kwargs):
-            super().__init__(*args, **kwargs)
-            if not cls.__dict__.get('_is_protocol', None):
-                cls._is_protocol = any(b is Protocol or
-                                       isinstance(b, _ProtocolMeta) and
-                                       b.__origin__ is Protocol
-                                       for b in cls.__bases__)
-            if cls._is_protocol:
-                for base in cls.__mro__[1:]:
-                    if not (base in (object, typing.Generic) or
-                            base.__module__ == 'collections.abc' and
-                            base.__name__ in _PROTO_WHITELIST or
-                            isinstance(base, typing.TypingMeta) and base._is_protocol or
-                            isinstance(base, GenericMeta) and
-                            base.__origin__ is typing.Generic):
-                        raise TypeError(f'Protocols can only inherit from other'
-                                        f' protocols, got {repr(base)}')
-
-                cls.__init__ = _no_init
-
-            def _proto_hook(other):
-                if not cls.__dict__.get('_is_protocol', None):
-                    return NotImplemented
-                if not isinstance(other, type):
-                    # Same error as for issubclass(1, int)
-                    raise TypeError('issubclass() arg 1 must be a class')
-                for attr in _get_protocol_attrs(cls):
-                    for base in other.__mro__:
-                        if attr in base.__dict__:
-                            if base.__dict__[attr] is None:
-                                return NotImplemented
-                            break
-                        annotations = getattr(base, '__annotations__', {})
-                        if (isinstance(annotations, typing.Mapping) and
-                                attr in annotations and
-                                isinstance(other, _ProtocolMeta) and
-                                other._is_protocol):
-                            break
-                    else:
-                        return NotImplemented
-                return True
-            if '__subclasshook__' not in cls.__dict__:
-                cls.__subclasshook__ = _proto_hook
-
-        def __instancecheck__(self, instance):
-            # We need this method for situations where attributes are
-            # assigned in __init__.
-            if ((not getattr(self, '_is_protocol', False) or
-                    _is_callable_members_only(self)) and
-                    issubclass(instance.__class__, self)):
-                return True
-            if self._is_protocol:
-                if all(hasattr(instance, attr) and
-                        (not callable(getattr(self, attr, None)) or
-                         getattr(instance, attr) is not None)
-                        for attr in _get_protocol_attrs(self)):
-                    return True
-            return super(GenericMeta, self).__instancecheck__(instance)
-
-        def __subclasscheck__(self, cls):
-            if self.__origin__ is not None:
-                if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']:
-                    raise TypeError("Parameterized generics cannot be used with class "
-                                    "or instance checks")
-                return False
-            if (self.__dict__.get('_is_protocol', None) and
-                    not self.__dict__.get('_is_runtime_protocol', None)):
-                if sys._getframe(1).f_globals['__name__'] in ['abc',
-                                                              'functools',
-                                                              'typing']:
-                    return False
-                raise TypeError("Instance and class checks can only be used with"
-                                " @runtime protocols")
-            if (self.__dict__.get('_is_runtime_protocol', None) and
-                    not _is_callable_members_only(self)):
-                if sys._getframe(1).f_globals['__name__'] in ['abc',
-                                                              'functools',
-                                                              'typing']:
-                    return super(GenericMeta, self).__subclasscheck__(cls)
-                raise TypeError("Protocols with non-method members"
-                                " don't support issubclass()")
-            return super(GenericMeta, self).__subclasscheck__(cls)
-
-        @typing._tp_cache
-        def __getitem__(self, params):
-            # We also need to copy this from GenericMeta.__getitem__ to get
-            # special treatment of "Protocol". (Comments removed for brevity.)
-            if not isinstance(params, tuple):
-                params = (params,)
-            if not params and _gorg(self) is not typing.Tuple:
-                raise TypeError(
-                    f"Parameter list to {self.__qualname__}[...] cannot be empty")
-            msg = "Parameters to generic types must be types."
-            params = tuple(_type_check(p, msg) for p in params)
-            if self in (typing.Generic, Protocol):
-                if not all(isinstance(p, typing.TypeVar) for p in params):
-                    raise TypeError(
-                        f"Parameters to {repr(self)}[...] must all be type variables")
-                if len(set(params)) != len(params):
-                    raise TypeError(
-                        f"Parameters to {repr(self)}[...] must all be unique")
-                tvars = params
-                args = params
-            elif self in (typing.Tuple, typing.Callable):
-                tvars = _type_vars(params)
-                args = params
-            elif self.__origin__ in (typing.Generic, Protocol):
-                raise TypeError(f"Cannot subscript already-subscripted {repr(self)}")
-            else:
-                _check_generic(self, params)
-                tvars = _type_vars(params)
-                args = params
-
-            prepend = (self,) if self.__origin__ is None else ()
-            return self.__class__(self.__name__,
-                                  prepend + self.__bases__,
-                                  _no_slots_copy(self.__dict__),
-                                  tvars=tvars,
-                                  args=args,
-                                  origin=self,
-                                  extra=self.__extra__,
-                                  orig_bases=self.__orig_bases__)
-
-    class Protocol(metaclass=_ProtocolMeta):
-        """Base class for protocol classes. Protocol classes are defined as::
-
-          class Proto(Protocol):
-              def meth(self) -> int:
-                  ...
-
-        Such classes are primarily used with static type checkers that recognize
-        structural subtyping (static duck-typing), for example::
-
-          class C:
-              def meth(self) -> int:
-                  return 0
-
-          def func(x: Proto) -> int:
-              return x.meth()
-
-          func(C())  # Passes static type check
-
-        See PEP 544 for details. Protocol classes decorated with
-        @typing_extensions.runtime act as simple-minded runtime protocol that checks
-        only the presence of given attributes, ignoring their type signatures.
-
-        Protocol classes can be generic, they are defined as::
-
-          class GenProto(Protocol[T]):
-              def meth(self) -> T:
-                  ...
-        """
-        __slots__ = ()
-        _is_protocol = True
-
-        def __new__(cls, *args, **kwds):
-            if _gorg(cls) is Protocol:
-                raise TypeError("Type Protocol cannot be instantiated; "
-                                "it can be used only as a base class")
-            return typing._generic_new(cls.__next_in_mro__, cls, *args, **kwds)
-
-
-# 3.8+
-if hasattr(typing, 'runtime_checkable'):
-    runtime_checkable = typing.runtime_checkable
-# 3.6-3.7
-else:
-    def runtime_checkable(cls):
-        """Mark a protocol class as a runtime protocol, so that it
-        can be used with isinstance() and issubclass(). Raise TypeError
-        if applied to a non-protocol class.
-
-        This allows a simple-minded structural check very similar to the
-        one-offs in collections.abc such as Hashable.
-        """
-        if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol:
-            raise TypeError('@runtime_checkable can be only applied to protocol classes,'
-                            f' got {cls!r}')
-        cls._is_runtime_protocol = True
-        return cls
-
-
-# Exists for backwards compatibility.
-runtime = runtime_checkable
-
-
-# 3.8+
-if hasattr(typing, 'SupportsIndex'):
-    SupportsIndex = typing.SupportsIndex
-# 3.6-3.7
-else:
-    @runtime_checkable
-    class SupportsIndex(Protocol):
-        __slots__ = ()
-
-        @abc.abstractmethod
-        def __index__(self) -> int:
-            pass
-
-
-if sys.version_info >= (3, 9, 2):
-    # The standard library TypedDict in Python 3.8 does not store runtime information
-    # about which (if any) keys are optional.  See https://bugs.python.org/issue38834
-    # The standard library TypedDict in Python 3.9.0/1 does not honour the "total"
-    # keyword with old-style TypedDict().  See https://bugs.python.org/issue42059
-    TypedDict = typing.TypedDict
-else:
-    def _check_fails(cls, other):
-        try:
-            if sys._getframe(1).f_globals['__name__'] not in ['abc',
-                                                              'functools',
-                                                              'typing']:
-                # Typed dicts are only for static structural subtyping.
-                raise TypeError('TypedDict does not support instance and class checks')
-        except (AttributeError, ValueError):
-            pass
-        return False
-
-    def _dict_new(*args, **kwargs):
-        if not args:
-            raise TypeError('TypedDict.__new__(): not enough arguments')
-        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
-        return dict(*args, **kwargs)
-
-    _dict_new.__text_signature__ = '($cls, _typename, _fields=None, /, **kwargs)'
-
-    def _typeddict_new(*args, total=True, **kwargs):
-        if not args:
-            raise TypeError('TypedDict.__new__(): not enough arguments')
-        _, args = args[0], args[1:]  # allow the "cls" keyword be passed
-        if args:
-            typename, args = args[0], args[1:]  # allow the "_typename" keyword be passed
-        elif '_typename' in kwargs:
-            typename = kwargs.pop('_typename')
-            import warnings
-            warnings.warn("Passing '_typename' as keyword argument is deprecated",
-                          DeprecationWarning, stacklevel=2)
-        else:
-            raise TypeError("TypedDict.__new__() missing 1 required positional "
-                            "argument: '_typename'")
-        if args:
-            try:
-                fields, = args  # allow the "_fields" keyword be passed
-            except ValueError:
-                raise TypeError('TypedDict.__new__() takes from 2 to 3 '
-                                f'positional arguments but {len(args) + 2} '
-                                'were given')
-        elif '_fields' in kwargs and len(kwargs) == 1:
-            fields = kwargs.pop('_fields')
-            import warnings
-            warnings.warn("Passing '_fields' as keyword argument is deprecated",
-                          DeprecationWarning, stacklevel=2)
-        else:
-            fields = None
-
-        if fields is None:
-            fields = kwargs
-        elif kwargs:
-            raise TypeError("TypedDict takes either a dict or keyword arguments,"
-                            " but not both")
-
-        ns = {'__annotations__': dict(fields)}
-        try:
-            # Setting correct module is necessary to make typed dict classes pickleable.
-            ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__')
-        except (AttributeError, ValueError):
-            pass
-
-        return _TypedDictMeta(typename, (), ns, total=total)
-
-    _typeddict_new.__text_signature__ = ('($cls, _typename, _fields=None,'
-                                         ' /, *, total=True, **kwargs)')
-
-    class _TypedDictMeta(type):
-        def __init__(cls, name, bases, ns, total=True):
-            super().__init__(name, bases, ns)
-
-        def __new__(cls, name, bases, ns, total=True):
-            # Create new typed dict class object.
-            # This method is called directly when TypedDict is subclassed,
-            # or via _typeddict_new when TypedDict is instantiated. This way
-            # TypedDict supports all three syntaxes described in its docstring.
-            # Subclasses and instances of TypedDict return actual dictionaries
-            # via _dict_new.
-            ns['__new__'] = _typeddict_new if name == 'TypedDict' else _dict_new
-            tp_dict = super().__new__(cls, name, (dict,), ns)
-
-            annotations = {}
-            own_annotations = ns.get('__annotations__', {})
-            own_annotation_keys = set(own_annotations.keys())
-            msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
-            own_annotations = {
-                n: typing._type_check(tp, msg) for n, tp in own_annotations.items()
-            }
-            required_keys = set()
-            optional_keys = set()
-
-            for base in bases:
-                annotations.update(base.__dict__.get('__annotations__', {}))
-                required_keys.update(base.__dict__.get('__required_keys__', ()))
-                optional_keys.update(base.__dict__.get('__optional_keys__', ()))
-
-            annotations.update(own_annotations)
-            if total:
-                required_keys.update(own_annotation_keys)
-            else:
-                optional_keys.update(own_annotation_keys)
-
-            tp_dict.__annotations__ = annotations
-            tp_dict.__required_keys__ = frozenset(required_keys)
-            tp_dict.__optional_keys__ = frozenset(optional_keys)
-            if not hasattr(tp_dict, '__total__'):
-                tp_dict.__total__ = total
-            return tp_dict
-
-        __instancecheck__ = __subclasscheck__ = _check_fails
-
-    TypedDict = _TypedDictMeta('TypedDict', (dict,), {})
-    TypedDict.__module__ = __name__
-    TypedDict.__doc__ = \
-        """A simple typed name space. At runtime it is equivalent to a plain dict.
-
-        TypedDict creates a dictionary type that expects all of its
-        instances to have a certain set of keys, with each key
-        associated with a value of a consistent type. This expectation
-        is not checked at runtime but is only enforced by type checkers.
-        Usage::
-
-            class Point2D(TypedDict):
-                x: int
-                y: int
-                label: str
-
-            a: Point2D = {'x': 1, 'y': 2, 'label': 'good'}  # OK
-            b: Point2D = {'z': 3, 'label': 'bad'}           # Fails type check
-
-            assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
-
-        The type info can be accessed via the Point2D.__annotations__ dict, and
-        the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
-        TypedDict supports two additional equivalent forms::
-
-            Point2D = TypedDict('Point2D', x=int, y=int, label=str)
-            Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str})
-
-        The class syntax is only supported in Python 3.6+, while two other
-        syntax forms work for Python 2.7 and 3.2+
-        """
-
-
-# Python 3.9+ has PEP 593 (Annotated and modified get_type_hints)
-if hasattr(typing, 'Annotated'):
-    Annotated = typing.Annotated
-    get_type_hints = typing.get_type_hints
-    # Not exported and not a public API, but needed for get_origin() and get_args()
-    # to work.
-    _AnnotatedAlias = typing._AnnotatedAlias
-# 3.7-3.8
-elif PEP_560:
-    class _AnnotatedAlias(typing._GenericAlias, _root=True):
-        """Runtime representation of an annotated type.
-
-        At its core 'Annotated[t, dec1, dec2, ...]' is an alias for the type 't'
-        with extra annotations. The alias behaves like a normal typing alias,
-        instantiating is the same as instantiating the underlying type, binding
-        it to types is also the same.
-        """
-        def __init__(self, origin, metadata):
-            if isinstance(origin, _AnnotatedAlias):
-                metadata = origin.__metadata__ + metadata
-                origin = origin.__origin__
-            super().__init__(origin, origin)
-            self.__metadata__ = metadata
-
-        def copy_with(self, params):
-            assert len(params) == 1
-            new_type = params[0]
-            return _AnnotatedAlias(new_type, self.__metadata__)
-
-        def __repr__(self):
-            return (f"typing_extensions.Annotated[{typing._type_repr(self.__origin__)}, "
-                    f"{', '.join(repr(a) for a in self.__metadata__)}]")
-
-        def __reduce__(self):
-            return operator.getitem, (
-                Annotated, (self.__origin__,) + self.__metadata__
-            )
-
-        def __eq__(self, other):
-            if not isinstance(other, _AnnotatedAlias):
-                return NotImplemented
-            if self.__origin__ != other.__origin__:
-                return False
-            return self.__metadata__ == other.__metadata__
-
-        def __hash__(self):
-            return hash((self.__origin__, self.__metadata__))
-
-    class Annotated:
-        """Add context specific metadata to a type.
-
-        Example: Annotated[int, runtime_check.Unsigned] indicates to the
-        hypothetical runtime_check module that this type is an unsigned int.
-        Every other consumer of this type can ignore this metadata and treat
-        this type as int.
-
-        The first argument to Annotated must be a valid type (and will be in
-        the __origin__ field), the remaining arguments are kept as a tuple in
-        the __extra__ field.
-
-        Details:
-
-        - It's an error to call `Annotated` with less than two arguments.
-        - Nested Annotated are flattened::
-
-            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
-
-        - Instantiating an annotated type is equivalent to instantiating the
-        underlying type::
-
-            Annotated[C, Ann1](5) == C(5)
-
-        - Annotated can be used as a generic type alias::
-
-            Optimized = Annotated[T, runtime.Optimize()]
-            Optimized[int] == Annotated[int, runtime.Optimize()]
-
-            OptimizedList = Annotated[List[T], runtime.Optimize()]
-            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
-        """
-
-        __slots__ = ()
-
-        def __new__(cls, *args, **kwargs):
-            raise TypeError("Type Annotated cannot be instantiated.")
-
-        @typing._tp_cache
-        def __class_getitem__(cls, params):
-            if not isinstance(params, tuple) or len(params) < 2:
-                raise TypeError("Annotated[...] should be used "
-                                "with at least two arguments (a type and an "
-                                "annotation).")
-            msg = "Annotated[t, ...]: t must be a type."
-            origin = typing._type_check(params[0], msg)
-            metadata = tuple(params[1:])
-            return _AnnotatedAlias(origin, metadata)
-
-        def __init_subclass__(cls, *args, **kwargs):
-            raise TypeError(
-                f"Cannot subclass {cls.__module__}.Annotated"
-            )
-
-    def _strip_annotations(t):
-        """Strips the annotations from a given type.
-        """
-        if isinstance(t, _AnnotatedAlias):
-            return _strip_annotations(t.__origin__)
-        if isinstance(t, typing._GenericAlias):
-            stripped_args = tuple(_strip_annotations(a) for a in t.__args__)
-            if stripped_args == t.__args__:
-                return t
-            res = t.copy_with(stripped_args)
-            res._special = t._special
-            return res
-        return t
-
-    def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
-        """Return type hints for an object.
-
-        This is often the same as obj.__annotations__, but it handles
-        forward references encoded as string literals, adds Optional[t] if a
-        default value equal to None is set and recursively replaces all
-        'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
-
-        The argument may be a module, class, method, or function. The annotations
-        are returned as a dictionary. For classes, annotations include also
-        inherited members.
-
-        TypeError is raised if the argument is not of a type that can contain
-        annotations, and an empty dictionary is returned if no annotations are
-        present.
-
-        BEWARE -- the behavior of globalns and localns is counterintuitive
-        (unless you are familiar with how eval() and exec() work).  The
-        search order is locals first, then globals.
-
-        - If no dict arguments are passed, an attempt is made to use the
-          globals from obj (or the respective module's globals for classes),
-          and these are also used as the locals.  If the object does not appear
-          to have globals, an empty dictionary is used.
-
-        - If one dict argument is passed, it is used for both globals and
-          locals.
-
-        - If two dict arguments are passed, they specify globals and
-          locals, respectively.
-        """
-        hint = typing.get_type_hints(obj, globalns=globalns, localns=localns)
-        if include_extras:
-            return hint
-        return {k: _strip_annotations(t) for k, t in hint.items()}
-# 3.6
-else:
-
-    def _is_dunder(name):
-        """Returns True if name is a __dunder_variable_name__."""
-        return len(name) > 4 and name.startswith('__') and name.endswith('__')
-
-    # Prior to Python 3.7 types did not have `copy_with`. A lot of the equality
-    # checks, argument expansion etc. are done on the _subs_tre. As a result we
-    # can't provide a get_type_hints function that strips out annotations.
-
-    class AnnotatedMeta(typing.GenericMeta):
-        """Metaclass for Annotated"""
-
-        def __new__(cls, name, bases, namespace, **kwargs):
-            if any(b is not object for b in bases):
-                raise TypeError("Cannot subclass " + str(Annotated))
-            return super().__new__(cls, name, bases, namespace, **kwargs)
-
-        @property
-        def __metadata__(self):
-            return self._subs_tree()[2]
-
-        def _tree_repr(self, tree):
-            cls, origin, metadata = tree
-            if not isinstance(origin, tuple):
-                tp_repr = typing._type_repr(origin)
-            else:
-                tp_repr = origin[0]._tree_repr(origin)
-            metadata_reprs = ", ".join(repr(arg) for arg in metadata)
-            return f'{cls}[{tp_repr}, {metadata_reprs}]'
-
-        def _subs_tree(self, tvars=None, args=None):  # noqa
-            if self is Annotated:
-                return Annotated
-            res = super()._subs_tree(tvars=tvars, args=args)
-            # Flatten nested Annotated
-            if isinstance(res[1], tuple) and res[1][0] is Annotated:
-                sub_tp = res[1][1]
-                sub_annot = res[1][2]
-                return (Annotated, sub_tp, sub_annot + res[2])
-            return res
-
-        def _get_cons(self):
-            """Return the class used to create instance of this type."""
-            if self.__origin__ is None:
-                raise TypeError("Cannot get the underlying type of a "
-                                "non-specialized Annotated type.")
-            tree = self._subs_tree()
-            while isinstance(tree, tuple) and tree[0] is Annotated:
-                tree = tree[1]
-            if isinstance(tree, tuple):
-                return tree[0]
-            else:
-                return tree
-
-        @typing._tp_cache
-        def __getitem__(self, params):
-            if not isinstance(params, tuple):
-                params = (params,)
-            if self.__origin__ is not None:  # specializing an instantiated type
-                return super().__getitem__(params)
-            elif not isinstance(params, tuple) or len(params) < 2:
-                raise TypeError("Annotated[...] should be instantiated "
-                                "with at least two arguments (a type and an "
-                                "annotation).")
-            else:
-                msg = "Annotated[t, ...]: t must be a type."
-                tp = typing._type_check(params[0], msg)
-                metadata = tuple(params[1:])
-            return self.__class__(
-                self.__name__,
-                self.__bases__,
-                _no_slots_copy(self.__dict__),
-                tvars=_type_vars((tp,)),
-                # Metadata is a tuple so it won't be touched by _replace_args et al.
-                args=(tp, metadata),
-                origin=self,
-            )
-
-        def __call__(self, *args, **kwargs):
-            cons = self._get_cons()
-            result = cons(*args, **kwargs)
-            try:
-                result.__orig_class__ = self
-            except AttributeError:
-                pass
-            return result
-
-        def __getattr__(self, attr):
-            # For simplicity we just don't relay all dunder names
-            if self.__origin__ is not None and not _is_dunder(attr):
-                return getattr(self._get_cons(), attr)
-            raise AttributeError(attr)
-
-        def __setattr__(self, attr, value):
-            if _is_dunder(attr) or attr.startswith('_abc_'):
-                super().__setattr__(attr, value)
-            elif self.__origin__ is None:
-                raise AttributeError(attr)
-            else:
-                setattr(self._get_cons(), attr, value)
-
-        def __instancecheck__(self, obj):
-            raise TypeError("Annotated cannot be used with isinstance().")
-
-        def __subclasscheck__(self, cls):
-            raise TypeError("Annotated cannot be used with issubclass().")
-
-    class Annotated(metaclass=AnnotatedMeta):
-        """Add context specific metadata to a type.
-
-        Example: Annotated[int, runtime_check.Unsigned] indicates to the
-        hypothetical runtime_check module that this type is an unsigned int.
-        Every other consumer of this type can ignore this metadata and treat
-        this type as int.
-
-        The first argument to Annotated must be a valid type, the remaining
-        arguments are kept as a tuple in the __metadata__ field.
-
-        Details:
-
-        - It's an error to call `Annotated` with less than two arguments.
-        - Nested Annotated are flattened::
-
-            Annotated[Annotated[T, Ann1, Ann2], Ann3] == Annotated[T, Ann1, Ann2, Ann3]
-
-        - Instantiating an annotated type is equivalent to instantiating the
-        underlying type::
-
-            Annotated[C, Ann1](5) == C(5)
-
-        - Annotated can be used as a generic type alias::
-
-            Optimized = Annotated[T, runtime.Optimize()]
-            Optimized[int] == Annotated[int, runtime.Optimize()]
-
-            OptimizedList = Annotated[List[T], runtime.Optimize()]
-            OptimizedList[int] == Annotated[List[int], runtime.Optimize()]
-        """
-
-# Python 3.8 has get_origin() and get_args() but those implementations aren't
-# Annotated-aware, so we can't use those. Python 3.9's versions don't support
-# ParamSpecArgs and ParamSpecKwargs, so only Python 3.10's versions will do.
-if sys.version_info[:2] >= (3, 10):
-    get_origin = typing.get_origin
-    get_args = typing.get_args
-# 3.7-3.9
-elif PEP_560:
-    try:
-        # 3.9+
-        from typing import _BaseGenericAlias
-    except ImportError:
-        _BaseGenericAlias = typing._GenericAlias
-    try:
-        # 3.9+
-        from typing import GenericAlias
-    except ImportError:
-        GenericAlias = typing._GenericAlias
-
-    def get_origin(tp):
-        """Get the unsubscripted version of a type.
-
-        This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar
-        and Annotated. Return None for unsupported types. Examples::
-
-            get_origin(Literal[42]) is Literal
-            get_origin(int) is None
-            get_origin(ClassVar[int]) is ClassVar
-            get_origin(Generic) is Generic
-            get_origin(Generic[T]) is Generic
-            get_origin(Union[T, int]) is Union
-            get_origin(List[Tuple[T, T]][int]) == list
-            get_origin(P.args) is P
-        """
-        if isinstance(tp, _AnnotatedAlias):
-            return Annotated
-        if isinstance(tp, (typing._GenericAlias, GenericAlias, _BaseGenericAlias,
-                           ParamSpecArgs, ParamSpecKwargs)):
-            return tp.__origin__
-        if tp is typing.Generic:
-            return typing.Generic
-        return None
-
-    def get_args(tp):
-        """Get type arguments with all substitutions performed.
-
-        For unions, basic simplifications used by Union constructor are performed.
-        Examples::
-            get_args(Dict[str, int]) == (str, int)
-            get_args(int) == ()
-            get_args(Union[int, Union[T, int], str][int]) == (int, str)
-            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
-            get_args(Callable[[], T][int]) == ([], int)
-        """
-        if isinstance(tp, _AnnotatedAlias):
-            return (tp.__origin__,) + tp.__metadata__
-        if isinstance(tp, (typing._GenericAlias, GenericAlias)):
-            if getattr(tp, "_special", False):
-                return ()
-            res = tp.__args__
-            if get_origin(tp) is collections.abc.Callable and res[0] is not Ellipsis:
-                res = (list(res[:-1]), res[-1])
-            return res
-        return ()
-
-
-# 3.10+
-if hasattr(typing, 'TypeAlias'):
-    TypeAlias = typing.TypeAlias
-# 3.9
-elif sys.version_info[:2] >= (3, 9):
-    class _TypeAliasForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    @_TypeAliasForm
-    def TypeAlias(self, parameters):
-        """Special marker indicating that an assignment should
-        be recognized as a proper type alias definition by type
-        checkers.
-
-        For example::
-
-            Predicate: TypeAlias = Callable[..., bool]
-
-        It's invalid when used anywhere except as in the example above.
-        """
-        raise TypeError(f"{self} is not subscriptable")
-# 3.7-3.8
-elif sys.version_info[:2] >= (3, 7):
-    class _TypeAliasForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    TypeAlias = _TypeAliasForm('TypeAlias',
-                               doc="""Special marker indicating that an assignment should
-                               be recognized as a proper type alias definition by type
-                               checkers.
-
-                               For example::
-
-                                   Predicate: TypeAlias = Callable[..., bool]
-
-                               It's invalid when used anywhere except as in the example
-                               above.""")
-# 3.6
-else:
-    class _TypeAliasMeta(typing.TypingMeta):
-        """Metaclass for TypeAlias"""
-
-        def __repr__(self):
-            return 'typing_extensions.TypeAlias'
-
-    class _TypeAliasBase(typing._FinalTypingBase, metaclass=_TypeAliasMeta, _root=True):
-        """Special marker indicating that an assignment should
-        be recognized as a proper type alias definition by type
-        checkers.
-
-        For example::
-
-            Predicate: TypeAlias = Callable[..., bool]
-
-        It's invalid when used anywhere except as in the example above.
-        """
-        __slots__ = ()
-
-        def __instancecheck__(self, obj):
-            raise TypeError("TypeAlias cannot be used with isinstance().")
-
-        def __subclasscheck__(self, cls):
-            raise TypeError("TypeAlias cannot be used with issubclass().")
-
-        def __repr__(self):
-            return 'typing_extensions.TypeAlias'
-
-    TypeAlias = _TypeAliasBase(_root=True)
-
-
-# Python 3.10+ has PEP 612
-if hasattr(typing, 'ParamSpecArgs'):
-    ParamSpecArgs = typing.ParamSpecArgs
-    ParamSpecKwargs = typing.ParamSpecKwargs
-# 3.6-3.9
-else:
-    class _Immutable:
-        """Mixin to indicate that object should not be copied."""
-        __slots__ = ()
-
-        def __copy__(self):
-            return self
-
-        def __deepcopy__(self, memo):
-            return self
-
-    class ParamSpecArgs(_Immutable):
-        """The args for a ParamSpec object.
-
-        Given a ParamSpec object P, P.args is an instance of ParamSpecArgs.
-
-        ParamSpecArgs objects have a reference back to their ParamSpec:
-
-        P.args.__origin__ is P
-
-        This type is meant for runtime introspection and has no special meaning to
-        static type checkers.
-        """
-        def __init__(self, origin):
-            self.__origin__ = origin
-
-        def __repr__(self):
-            return f"{self.__origin__.__name__}.args"
-
-    class ParamSpecKwargs(_Immutable):
-        """The kwargs for a ParamSpec object.
-
-        Given a ParamSpec object P, P.kwargs is an instance of ParamSpecKwargs.
-
-        ParamSpecKwargs objects have a reference back to their ParamSpec:
-
-        P.kwargs.__origin__ is P
-
-        This type is meant for runtime introspection and has no special meaning to
-        static type checkers.
-        """
-        def __init__(self, origin):
-            self.__origin__ = origin
-
-        def __repr__(self):
-            return f"{self.__origin__.__name__}.kwargs"
-
-# 3.10+
-if hasattr(typing, 'ParamSpec'):
-    ParamSpec = typing.ParamSpec
-# 3.6-3.9
-else:
-
-    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
-    class ParamSpec(list):
-        """Parameter specification variable.
-
-        Usage::
-
-           P = ParamSpec('P')
-
-        Parameter specification variables exist primarily for the benefit of static
-        type checkers.  They are used to forward the parameter types of one
-        callable to another callable, a pattern commonly found in higher order
-        functions and decorators.  They are only valid when used in ``Concatenate``,
-        or s the first argument to ``Callable``. In Python 3.10 and higher,
-        they are also supported in user-defined Generics at runtime.
-        See class Generic for more information on generic types.  An
-        example for annotating a decorator::
-
-           T = TypeVar('T')
-           P = ParamSpec('P')
-
-           def add_logging(f: Callable[P, T]) -> Callable[P, T]:
-               '''A type-safe decorator to add logging to a function.'''
-               def inner(*args: P.args, **kwargs: P.kwargs) -> T:
-                   logging.info(f'{f.__name__} was called')
-                   return f(*args, **kwargs)
-               return inner
-
-           @add_logging
-           def add_two(x: float, y: float) -> float:
-               '''Add two numbers together.'''
-               return x + y
-
-        Parameter specification variables defined with covariant=True or
-        contravariant=True can be used to declare covariant or contravariant
-        generic types.  These keyword arguments are valid, but their actual semantics
-        are yet to be decided.  See PEP 612 for details.
-
-        Parameter specification variables can be introspected. e.g.:
-
-           P.__name__ == 'T'
-           P.__bound__ == None
-           P.__covariant__ == False
-           P.__contravariant__ == False
-
-        Note that only parameter specification variables defined in global scope can
-        be pickled.
-        """
-
-        # Trick Generic __parameters__.
-        __class__ = typing.TypeVar
-
-        @property
-        def args(self):
-            return ParamSpecArgs(self)
-
-        @property
-        def kwargs(self):
-            return ParamSpecKwargs(self)
-
-        def __init__(self, name, *, bound=None, covariant=False, contravariant=False):
-            super().__init__([self])
-            self.__name__ = name
-            self.__covariant__ = bool(covariant)
-            self.__contravariant__ = bool(contravariant)
-            if bound:
-                self.__bound__ = typing._type_check(bound, 'Bound must be a type.')
-            else:
-                self.__bound__ = None
-
-            # for pickling:
-            try:
-                def_mod = sys._getframe(1).f_globals.get('__name__', '__main__')
-            except (AttributeError, ValueError):
-                def_mod = None
-            if def_mod != 'typing_extensions':
-                self.__module__ = def_mod
-
-        def __repr__(self):
-            if self.__covariant__:
-                prefix = '+'
-            elif self.__contravariant__:
-                prefix = '-'
-            else:
-                prefix = '~'
-            return prefix + self.__name__
-
-        def __hash__(self):
-            return object.__hash__(self)
-
-        def __eq__(self, other):
-            return self is other
-
-        def __reduce__(self):
-            return self.__name__
-
-        # Hack to get typing._type_check to pass.
-        def __call__(self, *args, **kwargs):
-            pass
-
-        if not PEP_560:
-            # Only needed in 3.6.
-            def _get_type_vars(self, tvars):
-                if self not in tvars:
-                    tvars.append(self)
-
-
-# 3.6-3.9
-if not hasattr(typing, 'Concatenate'):
-    # Inherits from list as a workaround for Callable checks in Python < 3.9.2.
-    class _ConcatenateGenericAlias(list):
-
-        # Trick Generic into looking into this for __parameters__.
-        if PEP_560:
-            __class__ = typing._GenericAlias
-        else:
-            __class__ = typing._TypingBase
-
-        # Flag in 3.8.
-        _special = False
-        # Attribute in 3.6 and earlier.
-        _gorg = typing.Generic
-
-        def __init__(self, origin, args):
-            super().__init__(args)
-            self.__origin__ = origin
-            self.__args__ = args
-
-        def __repr__(self):
-            _type_repr = typing._type_repr
-            return (f'{_type_repr(self.__origin__)}'
-                    f'[{", ".join(_type_repr(arg) for arg in self.__args__)}]')
-
-        def __hash__(self):
-            return hash((self.__origin__, self.__args__))
-
-        # Hack to get typing._type_check to pass in Generic.
-        def __call__(self, *args, **kwargs):
-            pass
-
-        @property
-        def __parameters__(self):
-            return tuple(
-                tp for tp in self.__args__ if isinstance(tp, (typing.TypeVar, ParamSpec))
-            )
-
-        if not PEP_560:
-            # Only required in 3.6.
-            def _get_type_vars(self, tvars):
-                if self.__origin__ and self.__parameters__:
-                    typing._get_type_vars(self.__parameters__, tvars)
-
-
-# 3.6-3.9
-@typing._tp_cache
-def _concatenate_getitem(self, parameters):
-    if parameters == ():
-        raise TypeError("Cannot take a Concatenate of no types.")
-    if not isinstance(parameters, tuple):
-        parameters = (parameters,)
-    if not isinstance(parameters[-1], ParamSpec):
-        raise TypeError("The last parameter to Concatenate should be a "
-                        "ParamSpec variable.")
-    msg = "Concatenate[arg, ...]: each arg must be a type."
-    parameters = tuple(typing._type_check(p, msg) for p in parameters)
-    return _ConcatenateGenericAlias(self, parameters)
-
-
-# 3.10+
-if hasattr(typing, 'Concatenate'):
-    Concatenate = typing.Concatenate
-    _ConcatenateGenericAlias = typing._ConcatenateGenericAlias # noqa
-# 3.9
-elif sys.version_info[:2] >= (3, 9):
-    @_TypeAliasForm
-    def Concatenate(self, parameters):
-        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
-        higher order function which adds, removes or transforms parameters of a
-        callable.
-
-        For example::
-
-           Callable[Concatenate[int, P], int]
-
-        See PEP 612 for detailed information.
-        """
-        return _concatenate_getitem(self, parameters)
-# 3.7-8
-elif sys.version_info[:2] >= (3, 7):
-    class _ConcatenateForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            return _concatenate_getitem(self, parameters)
-
-    Concatenate = _ConcatenateForm(
-        'Concatenate',
-        doc="""Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
-        higher order function which adds, removes or transforms parameters of a
-        callable.
-
-        For example::
-
-           Callable[Concatenate[int, P], int]
-
-        See PEP 612 for detailed information.
-        """)
-# 3.6
-else:
-    class _ConcatenateAliasMeta(typing.TypingMeta):
-        """Metaclass for Concatenate."""
-
-        def __repr__(self):
-            return 'typing_extensions.Concatenate'
-
-    class _ConcatenateAliasBase(typing._FinalTypingBase,
-                                metaclass=_ConcatenateAliasMeta,
-                                _root=True):
-        """Used in conjunction with ``ParamSpec`` and ``Callable`` to represent a
-        higher order function which adds, removes or transforms parameters of a
-        callable.
-
-        For example::
-
-           Callable[Concatenate[int, P], int]
-
-        See PEP 612 for detailed information.
-        """
-        __slots__ = ()
-
-        def __instancecheck__(self, obj):
-            raise TypeError("Concatenate cannot be used with isinstance().")
-
-        def __subclasscheck__(self, cls):
-            raise TypeError("Concatenate cannot be used with issubclass().")
-
-        def __repr__(self):
-            return 'typing_extensions.Concatenate'
-
-        def __getitem__(self, parameters):
-            return _concatenate_getitem(self, parameters)
-
-    Concatenate = _ConcatenateAliasBase(_root=True)
-
-# 3.10+
-if hasattr(typing, 'TypeGuard'):
-    TypeGuard = typing.TypeGuard
-# 3.9
-elif sys.version_info[:2] >= (3, 9):
-    class _TypeGuardForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    @_TypeGuardForm
-    def TypeGuard(self, parameters):
-        """Special typing form used to annotate the return type of a user-defined
-        type guard function.  ``TypeGuard`` only accepts a single type argument.
-        At runtime, functions marked this way should return a boolean.
-
-        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
-        type checkers to determine a more precise type of an expression within a
-        program's code flow.  Usually type narrowing is done by analyzing
-        conditional code flow and applying the narrowing to a block of code.  The
-        conditional expression here is sometimes referred to as a "type guard".
-
-        Sometimes it would be convenient to use a user-defined boolean function
-        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
-        return type to alert static type checkers to this intention.
-
-        Using  ``-> TypeGuard`` tells the static type checker that for a given
-        function:
-
-        1. The return value is a boolean.
-        2. If the return value is ``True``, the type of its argument
-        is the type inside ``TypeGuard``.
-
-        For example::
-
-            def is_str(val: Union[str, float]):
-                # "isinstance" type guard
-                if isinstance(val, str):
-                    # Type of ``val`` is narrowed to ``str``
-                    ...
-                else:
-                    # Else, type of ``val`` is narrowed to ``float``.
-                    ...
-
-        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
-        form of ``TypeA`` (it can even be a wider form) and this may lead to
-        type-unsafe results.  The main reason is to allow for things like
-        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
-        a subtype of the former, since ``List`` is invariant.  The responsibility of
-        writing type-safe type guards is left to the user.
-
-        ``TypeGuard`` also works with type variables.  For more information, see
-        PEP 647 (User-Defined Type Guards).
-        """
-        item = typing._type_check(parameters, f'{self} accepts only single type.')
-        return typing._GenericAlias(self, (item,))
-# 3.7-3.8
-elif sys.version_info[:2] >= (3, 7):
-    class _TypeGuardForm(typing._SpecialForm, _root=True):
-
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            item = typing._type_check(parameters,
-                                      f'{self._name} accepts only a single type')
-            return typing._GenericAlias(self, (item,))
-
-    TypeGuard = _TypeGuardForm(
-        'TypeGuard',
-        doc="""Special typing form used to annotate the return type of a user-defined
-        type guard function.  ``TypeGuard`` only accepts a single type argument.
-        At runtime, functions marked this way should return a boolean.
-
-        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
-        type checkers to determine a more precise type of an expression within a
-        program's code flow.  Usually type narrowing is done by analyzing
-        conditional code flow and applying the narrowing to a block of code.  The
-        conditional expression here is sometimes referred to as a "type guard".
-
-        Sometimes it would be convenient to use a user-defined boolean function
-        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
-        return type to alert static type checkers to this intention.
-
-        Using  ``-> TypeGuard`` tells the static type checker that for a given
-        function:
-
-        1. The return value is a boolean.
-        2. If the return value is ``True``, the type of its argument
-        is the type inside ``TypeGuard``.
-
-        For example::
-
-            def is_str(val: Union[str, float]):
-                # "isinstance" type guard
-                if isinstance(val, str):
-                    # Type of ``val`` is narrowed to ``str``
-                    ...
-                else:
-                    # Else, type of ``val`` is narrowed to ``float``.
-                    ...
-
-        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
-        form of ``TypeA`` (it can even be a wider form) and this may lead to
-        type-unsafe results.  The main reason is to allow for things like
-        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
-        a subtype of the former, since ``List`` is invariant.  The responsibility of
-        writing type-safe type guards is left to the user.
-
-        ``TypeGuard`` also works with type variables.  For more information, see
-        PEP 647 (User-Defined Type Guards).
-        """)
-# 3.6
-else:
-    class _TypeGuard(typing._FinalTypingBase, _root=True):
-        """Special typing form used to annotate the return type of a user-defined
-        type guard function.  ``TypeGuard`` only accepts a single type argument.
-        At runtime, functions marked this way should return a boolean.
-
-        ``TypeGuard`` aims to benefit *type narrowing* -- a technique used by static
-        type checkers to determine a more precise type of an expression within a
-        program's code flow.  Usually type narrowing is done by analyzing
-        conditional code flow and applying the narrowing to a block of code.  The
-        conditional expression here is sometimes referred to as a "type guard".
-
-        Sometimes it would be convenient to use a user-defined boolean function
-        as a type guard.  Such a function should use ``TypeGuard[...]`` as its
-        return type to alert static type checkers to this intention.
-
-        Using  ``-> TypeGuard`` tells the static type checker that for a given
-        function:
-
-        1. The return value is a boolean.
-        2. If the return value is ``True``, the type of its argument
-        is the type inside ``TypeGuard``.
-
-        For example::
-
-            def is_str(val: Union[str, float]):
-                # "isinstance" type guard
-                if isinstance(val, str):
-                    # Type of ``val`` is narrowed to ``str``
-                    ...
-                else:
-                    # Else, type of ``val`` is narrowed to ``float``.
-                    ...
-
-        Strict type narrowing is not enforced -- ``TypeB`` need not be a narrower
-        form of ``TypeA`` (it can even be a wider form) and this may lead to
-        type-unsafe results.  The main reason is to allow for things like
-        narrowing ``List[object]`` to ``List[str]`` even though the latter is not
-        a subtype of the former, since ``List`` is invariant.  The responsibility of
-        writing type-safe type guards is left to the user.
-
-        ``TypeGuard`` also works with type variables.  For more information, see
-        PEP 647 (User-Defined Type Guards).
-        """
-
-        __slots__ = ('__type__',)
-
-        def __init__(self, tp=None, **kwds):
-            self.__type__ = tp
-
-        def __getitem__(self, item):
-            cls = type(self)
-            if self.__type__ is None:
-                return cls(typing._type_check(item,
-                           f'{cls.__name__[1:]} accepts only a single type.'),
-                           _root=True)
-            raise TypeError(f'{cls.__name__[1:]} cannot be further subscripted')
-
-        def _eval_type(self, globalns, localns):
-            new_tp = typing._eval_type(self.__type__, globalns, localns)
-            if new_tp == self.__type__:
-                return self
-            return type(self)(new_tp, _root=True)
-
-        def __repr__(self):
-            r = super().__repr__()
-            if self.__type__ is not None:
-                r += f'[{typing._type_repr(self.__type__)}]'
-            return r
-
-        def __hash__(self):
-            return hash((type(self).__name__, self.__type__))
-
-        def __eq__(self, other):
-            if not isinstance(other, _TypeGuard):
-                return NotImplemented
-            if self.__type__ is not None:
-                return self.__type__ == other.__type__
-            return self is other
-
-    TypeGuard = _TypeGuard(_root=True)
-
-if hasattr(typing, "Self"):
-    Self = typing.Self
-elif sys.version_info[:2] >= (3, 7):
-    # Vendored from cpython typing._SpecialFrom
-    class _SpecialForm(typing._Final, _root=True):
-        __slots__ = ('_name', '__doc__', '_getitem')
-
-        def __init__(self, getitem):
-            self._getitem = getitem
-            self._name = getitem.__name__
-            self.__doc__ = getitem.__doc__
-
-        def __getattr__(self, item):
-            if item in {'__name__', '__qualname__'}:
-                return self._name
-
-            raise AttributeError(item)
-
-        def __mro_entries__(self, bases):
-            raise TypeError(f"Cannot subclass {self!r}")
-
-        def __repr__(self):
-            return f'typing_extensions.{self._name}'
-
-        def __reduce__(self):
-            return self._name
-
-        def __call__(self, *args, **kwds):
-            raise TypeError(f"Cannot instantiate {self!r}")
-
-        def __or__(self, other):
-            return typing.Union[self, other]
-
-        def __ror__(self, other):
-            return typing.Union[other, self]
-
-        def __instancecheck__(self, obj):
-            raise TypeError(f"{self} cannot be used with isinstance()")
-
-        def __subclasscheck__(self, cls):
-            raise TypeError(f"{self} cannot be used with issubclass()")
-
-        @typing._tp_cache
-        def __getitem__(self, parameters):
-            return self._getitem(self, parameters)
-
-    @_SpecialForm
-    def Self(self, params):
-        """Used to spell the type of "self" in classes.
-
-        Example::
-
-          from typing import Self
-
-          class ReturnsSelf:
-              def parse(self, data: bytes) -> Self:
-                  ...
-                  return self
-
-        """
-
-        raise TypeError(f"{self} is not subscriptable")
-else:
-    class _Self(typing._FinalTypingBase, _root=True):
-        """Used to spell the type of "self" in classes.
-
-        Example::
-
-          from typing import Self
-
-          class ReturnsSelf:
-              def parse(self, data: bytes) -> Self:
-                  ...
-                  return self
-
-        """
-
-        __slots__ = ()
-
-        def __instancecheck__(self, obj):
-            raise TypeError(f"{self} cannot be used with isinstance().")
-
-        def __subclasscheck__(self, cls):
-            raise TypeError(f"{self} cannot be used with issubclass().")
-
-    Self = _Self(_root=True)
-
-
-if hasattr(typing, 'Required'):
-    Required = typing.Required
-    NotRequired = typing.NotRequired
-elif sys.version_info[:2] >= (3, 9):
-    class _ExtensionsSpecialForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-    @_ExtensionsSpecialForm
-    def Required(self, parameters):
-        """A special typing construct to mark a key of a total=False TypedDict
-        as required. For example:
-
-            class Movie(TypedDict, total=False):
-                title: Required[str]
-                year: int
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-
-        There is no runtime checking that a required key is actually provided
-        when instantiating a related TypedDict.
-        """
-        item = typing._type_check(parameters, f'{self._name} accepts only single type')
-        return typing._GenericAlias(self, (item,))
-
-    @_ExtensionsSpecialForm
-    def NotRequired(self, parameters):
-        """A special typing construct to mark a key of a TypedDict as
-        potentially missing. For example:
-
-            class Movie(TypedDict):
-                title: str
-                year: NotRequired[int]
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-        """
-        item = typing._type_check(parameters, f'{self._name} accepts only single type')
-        return typing._GenericAlias(self, (item,))
-
-elif sys.version_info[:2] >= (3, 7):
-    class _RequiredForm(typing._SpecialForm, _root=True):
-        def __repr__(self):
-            return 'typing_extensions.' + self._name
-
-        def __getitem__(self, parameters):
-            item = typing._type_check(parameters,
-                                      '{} accepts only single type'.format(self._name))
-            return typing._GenericAlias(self, (item,))
-
-    Required = _RequiredForm(
-        'Required',
-        doc="""A special typing construct to mark a key of a total=False TypedDict
-        as required. For example:
-
-            class Movie(TypedDict, total=False):
-                title: Required[str]
-                year: int
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-
-        There is no runtime checking that a required key is actually provided
-        when instantiating a related TypedDict.
-        """)
-    NotRequired = _RequiredForm(
-        'NotRequired',
-        doc="""A special typing construct to mark a key of a TypedDict as
-        potentially missing. For example:
-
-            class Movie(TypedDict):
-                title: str
-                year: NotRequired[int]
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-        """)
-else:
-    # NOTE: Modeled after _Final's implementation when _FinalTypingBase available
-    class _MaybeRequired(typing._FinalTypingBase, _root=True):
-        __slots__ = ('__type__',)
-
-        def __init__(self, tp=None, **kwds):
-            self.__type__ = tp
-
-        def __getitem__(self, item):
-            cls = type(self)
-            if self.__type__ is None:
-                return cls(typing._type_check(item,
-                           '{} accepts only single type.'.format(cls.__name__[1:])),
-                           _root=True)
-            raise TypeError('{} cannot be further subscripted'
-                            .format(cls.__name__[1:]))
-
-        def _eval_type(self, globalns, localns):
-            new_tp = typing._eval_type(self.__type__, globalns, localns)
-            if new_tp == self.__type__:
-                return self
-            return type(self)(new_tp, _root=True)
-
-        def __repr__(self):
-            r = super().__repr__()
-            if self.__type__ is not None:
-                r += '[{}]'.format(typing._type_repr(self.__type__))
-            return r
-
-        def __hash__(self):
-            return hash((type(self).__name__, self.__type__))
-
-        def __eq__(self, other):
-            if not isinstance(other, type(self)):
-                return NotImplemented
-            if self.__type__ is not None:
-                return self.__type__ == other.__type__
-            return self is other
-
-    class _Required(_MaybeRequired, _root=True):
-        """A special typing construct to mark a key of a total=False TypedDict
-        as required. For example:
-
-            class Movie(TypedDict, total=False):
-                title: Required[str]
-                year: int
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-
-        There is no runtime checking that a required key is actually provided
-        when instantiating a related TypedDict.
-        """
-
-    class _NotRequired(_MaybeRequired, _root=True):
-        """A special typing construct to mark a key of a TypedDict as
-        potentially missing. For example:
-
-            class Movie(TypedDict):
-                title: str
-                year: NotRequired[int]
-
-            m = Movie(
-                title='The Matrix',  # typechecker error if key is omitted
-                year=1999,
-            )
-        """
-
-    Required = _Required(_root=True)
-    NotRequired = _NotRequired(_root=True)
diff --git a/setuptools/_vendor/vendored.txt b/setuptools/_vendor/vendored.txt
index e67c7845c8..7255f98aee 100644
--- a/setuptools/_vendor/vendored.txt
+++ b/setuptools/_vendor/vendored.txt
@@ -4,8 +4,6 @@ more_itertools==8.8.0
 jaraco.text==3.7.0
 importlib_resources==5.10.2
 importlib_metadata==6.0.0
-# required for importlib_metadata on older Pythons
-typing_extensions==4.0.1
 # required for importlib_resources and _metadata on older Pythons
 zipp==3.7.0
 tomli==2.0.1
diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py
index 16e2c9ea9e..3e66269bec 100644
--- a/setuptools/extern/__init__.py
+++ b/setuptools/extern/__init__.py
@@ -83,7 +83,6 @@ def install(self):
     'jaraco',
     'importlib_resources',
     'importlib_metadata',
-    'typing_extensions',
     'zipp',
     'tomli',
     'backports',
diff --git a/tools/vendored.py b/tools/vendored.py
index 63797ea24a..7d4399bbdc 100644
--- a/tools/vendored.py
+++ b/tools/vendored.py
@@ -82,7 +82,7 @@ def rewrite_importlib_metadata(pkg_files, new_root):
     Rewrite imports in importlib_metadata to redirect to vendored copies.
     """
     for file in pkg_files.glob('*.py'):
-        text = file.read_text().replace('typing_extensions', '..typing_extensions')
+        text = file.read_text()
         text = text.replace('import zipp', 'from .. import zipp')
         file.write_text(text)
 
@@ -109,7 +109,6 @@ def rewrite_platformdirs(pkg_files: Path):
     init = pkg_files.joinpath('__init__.py')
     text = init.read_text()
     text = text.replace('from platformdirs.', 'from .')
-    text = text.replace('from typing_extensions', 'from ..typing_extensions')
     init.write_text(text)
 
 

From 44bf554feed0b6cf88d2f9465006ed47626f06d6 Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Tue, 7 May 2024 15:58:44 -0400
Subject: [PATCH 105/135] Fix Ruff

---
 pkg_resources/__init__.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index a66e701a3d..1cd657fb6d 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -27,7 +27,7 @@
 import time
 import re
 import types
-from typing import Any, Callable, Dict, Iterable, List, Protocol, Optional, TypeVar
+from typing import Callable, Dict, Iterable, List, Protocol, Optional, TypeVar
 import zipfile
 import zipimport
 import warnings

From a3fa4679fc21f33e7eed6b5817ef81a10354bfd4 Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Tue, 7 May 2024 16:03:11 -0400
Subject: [PATCH 106/135] Update newsfragments/4324.removal.rst

---
 newsfragments/4324.removal.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/newsfragments/4324.removal.rst b/newsfragments/4324.removal.rst
index bd5e1cb641..3782a0b81b 100644
--- a/newsfragments/4324.removal.rst
+++ b/newsfragments/4324.removal.rst
@@ -1 +1 @@
-Removed `typing_extensions` from vendored dependencies -- by :user:`Avasam`
+Removed ``typing_extensions`` from vendored dependencies -- by :user:`Avasam`

From ed7a12c602f44db068ef4063a51fccdf3245f947 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Thu, 9 May 2024 15:32:31 +0100
Subject: [PATCH 107/135] Simplify `mypy.ini`

According to review suggestion

Co-authored-by: Avasam 
---
 mypy.ini | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/mypy.ini b/mypy.ini
index 9fab958288..3a26daf1c9 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -31,11 +31,7 @@ ignore_missing_imports = True
 
 # - pkg_resources tests create modules that won't exists statically before the test is run.
 #   Let's ignore all "import-not-found" since, if an import really wasn't found, then the test would fail.
-# - setuptools._vendor.packaging._manylinux: Mypy issue, this vendored module is already excluded!
+# Even when excluding vendored/generated modules, there might be problems: https://github.com/python/mypy/issues/11936#issuecomment-1466764006
 [mypy-pkg_resources.tests.*,setuptools._vendor.packaging._manylinux,setuptools.config._validate_pyproject.*]
-disable_error_code = import-not-found
-
-# - The unused-ignore comment in setuptools.config._validate_pyproject.* is probably evaluated differently
-#   in different versions of Python. Also, this file should already be ignored...
-[mypy-setuptools.config._validate_pyproject.*]
-disable_error_code = unused-ignore
+follow_imports = silent
+# silent => ignore errors when following imports

From 5c9d37abdb3f112a502340a75c4ddd91e5950c5c Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Thu, 9 May 2024 16:14:17 +0100
Subject: [PATCH 108/135] Fix mypy error

---
 mypy.ini | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/mypy.ini b/mypy.ini
index 3a26daf1c9..de545a103c 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -21,6 +21,11 @@ exclude = (?x)(
 # *.extern modules that actually live in *._vendor will also cause attr-defined issues on import
 disable_error_code = attr-defined
 
+# - pkg_resources tests create modules that won't exists statically before the test is run.
+#   Let's ignore all "import-not-found" since, if an import really wasn't found, then the test would fail.
+[mypy-pkg_resources.tests.*]
+disable_error_code = import-not-found
+
 # - Avoid raising issues when importing from "extern" modules, as those are added to path dynamically.
 #   https://github.com/pypa/setuptools/pull/3979#discussion_r1367968993
 # - distutils._modified has different errors on Python 3.8 [import-untyped], on Python 3.9+ [import-not-found]
@@ -29,9 +34,7 @@ disable_error_code = attr-defined
 [mypy-pkg_resources.extern.*,setuptools.extern.*,distutils._modified,jaraco.*,trove_classifiers]
 ignore_missing_imports = True
 
-# - pkg_resources tests create modules that won't exists statically before the test is run.
-#   Let's ignore all "import-not-found" since, if an import really wasn't found, then the test would fail.
 # Even when excluding vendored/generated modules, there might be problems: https://github.com/python/mypy/issues/11936#issuecomment-1466764006
-[mypy-pkg_resources.tests.*,setuptools._vendor.packaging._manylinux,setuptools.config._validate_pyproject.*]
+[mypy-setuptools._vendor.packaging._manylinux,setuptools.config._validate_pyproject.*]
 follow_imports = silent
 # silent => ignore errors when following imports

From e9995828311c5e0c843622ca2be85e7f09f1ff0d Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Thu, 9 May 2024 11:35:59 -0400
Subject: [PATCH 109/135] Update dynamic module imports in ``pkg_resources`` to
 private alias static imports Enabled ``attr-defined`` checks in mypy for
 ``pkg_resources``

---
 mypy.ini                              |  3 +-
 newsfragments/4348.bugfix.rst         |  1 +
 newsfragments/4348.misc.rst           |  1 +
 pkg_resources/__init__.py             | 44 ++++++++++++---------------
 pkg_resources/tests/test_resources.py |  6 ++--
 5 files changed, 26 insertions(+), 29 deletions(-)
 create mode 100644 newsfragments/4348.bugfix.rst
 create mode 100644 newsfragments/4348.misc.rst

diff --git a/mypy.ini b/mypy.ini
index 45671826b1..4eebe5f77c 100644
--- a/mypy.ini
+++ b/mypy.ini
@@ -15,10 +15,11 @@ exclude = (?x)(
 	| ^setuptools/_distutils/ # Vendored
 	| ^setuptools/config/_validate_pyproject/ # Auto-generated
 	)
+
 # Ignoring attr-defined because setuptools wraps a lot of distutils classes, adding new attributes,
 # w/o updating all the attributes and return types from the base classes for type-checkers to understand
 # Especially with setuptools.dist.command vs distutils.dist.command vs setuptools._distutils.dist.command
-# *.extern modules that actually live in *._vendor will also cause attr-defined issues on import
+[mypy-setuptools.*]
 disable_error_code = attr-defined
 
 # - Avoid raising issues when importing from "extern" modules, as those are added to path dynamically.
diff --git a/newsfragments/4348.bugfix.rst b/newsfragments/4348.bugfix.rst
new file mode 100644
index 0000000000..a8bb79a123
--- /dev/null
+++ b/newsfragments/4348.bugfix.rst
@@ -0,0 +1 @@
+Fix an error with `UnicodeDecodeError` handling in ``pkg_resources`` when trying to read files in UTF-8 with a fallback -- by :user:`Avasam`
diff --git a/newsfragments/4348.misc.rst b/newsfragments/4348.misc.rst
new file mode 100644
index 0000000000..989226c4b3
--- /dev/null
+++ b/newsfragments/4348.misc.rst
@@ -0,0 +1 @@
+Update dynamic module imports in ``pkg_resources`` to private alias static imports. Enabled ``attr-defined`` checks in mypy for ``pkg_resources`` -- by :user:`Avasam`
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 1cd657fb6d..b595ec5965 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -70,15 +70,11 @@
     drop_comment,
     join_continuation,
 )
-
-from pkg_resources.extern import platformdirs
-from pkg_resources.extern import packaging
-
-__import__('pkg_resources.extern.packaging.version')
-__import__('pkg_resources.extern.packaging.specifiers')
-__import__('pkg_resources.extern.packaging.requirements')
-__import__('pkg_resources.extern.packaging.markers')
-__import__('pkg_resources.extern.packaging.utils')
+from pkg_resources.extern.packaging import markers as _packaging_markers
+from pkg_resources.extern.packaging import requirements as _packaging_requirements
+from pkg_resources.extern.packaging import utils as _packaging_utils
+from pkg_resources.extern.packaging import version as _packaging_version
+from pkg_resources.extern.platformdirs import user_cache_dir
 
 # declare some globals that will be defined later to
 # satisfy the linters.
@@ -116,7 +112,7 @@ class PEP440Warning(RuntimeWarning):
     """
 
 
-parse_version = packaging.version.Version
+parse_version = _packaging_version.Version
 
 
 _state_vars: Dict[str, str] = {}
@@ -730,7 +726,7 @@ def add(self, dist, entry=None, insert=True, replace=False):
             return
 
         self.by_key[dist.key] = dist
-        normalized_name = packaging.utils.canonicalize_name(dist.key)
+        normalized_name = _packaging_utils.canonicalize_name(dist.key)
         self.normalized_to_canonical_keys[normalized_name] = dist.key
         if dist.key not in keys:
             keys.append(dist.key)
@@ -1344,9 +1340,7 @@ def get_default_cache():
     or a platform-relevant user cache dir for an app
     named "Python-Eggs".
     """
-    return os.environ.get('PYTHON_EGG_CACHE') or platformdirs.user_cache_dir(
-        appname='Python-Eggs'
-    )
+    return os.environ.get('PYTHON_EGG_CACHE') or user_cache_dir(appname='Python-Eggs')
 
 
 def safe_name(name):
@@ -1363,8 +1357,8 @@ def safe_version(version):
     """
     try:
         # normalize the version
-        return str(packaging.version.Version(version))
-    except packaging.version.InvalidVersion:
+        return str(_packaging_version.Version(version))
+    except _packaging_version.InvalidVersion:
         version = version.replace(' ', '.')
         return re.sub('[^A-Za-z0-9.]+', '-', version)
 
@@ -1441,9 +1435,9 @@ def evaluate_marker(text, extra=None):
     This implementation uses the 'pyparsing' module.
     """
     try:
-        marker = packaging.markers.Marker(text)
+        marker = _packaging_markers.Marker(text)
         return marker.evaluate()
-    except packaging.markers.InvalidMarker as e:
+    except _packaging_markers.InvalidMarker as e:
         raise SyntaxError(e) from e
 
 
@@ -2695,12 +2689,12 @@ def parsed_version(self):
         if not hasattr(self, "_parsed_version"):
             try:
                 self._parsed_version = parse_version(self.version)
-            except packaging.version.InvalidVersion as ex:
+            except _packaging_version.InvalidVersion as ex:
                 info = f"(package: {self.project_name})"
                 if hasattr(ex, "add_note"):
                     ex.add_note(info)  # PEP 678
                     raise
-                raise packaging.version.InvalidVersion(f"{str(ex)} {info}") from None
+                raise _packaging_version.InvalidVersion(f"{str(ex)} {info}") from None
 
         return self._parsed_version
 
@@ -2708,7 +2702,7 @@ def parsed_version(self):
     def _forgiving_parsed_version(self):
         try:
             return self.parsed_version
-        except packaging.version.InvalidVersion as ex:
+        except _packaging_version.InvalidVersion as ex:
             self._parsed_version = parse_version(_forgiving_version(self.version))
 
             notes = "\n".join(getattr(ex, "__notes__", []))  # PEP 678
@@ -2881,7 +2875,7 @@ def from_filename(cls, filename, metadata=None, **kw):
 
     def as_requirement(self):
         """Return a ``Requirement`` that matches this distribution exactly"""
-        if isinstance(self.parsed_version, packaging.version.Version):
+        if isinstance(self.parsed_version, _packaging_version.Version):
             spec = "%s==%s" % (self.project_name, self.parsed_version)
         else:
             spec = "%s===%s" % (self.project_name, self.parsed_version)
@@ -3127,11 +3121,11 @@ def parse_requirements(strs):
     return map(Requirement, join_continuation(map(drop_comment, yield_lines(strs))))
 
 
-class RequirementParseError(packaging.requirements.InvalidRequirement):
+class RequirementParseError(_packaging_requirements.InvalidRequirement):
     "Compatibility wrapper for InvalidRequirement"
 
 
-class Requirement(packaging.requirements.Requirement):
+class Requirement(_packaging_requirements.Requirement):
     def __init__(self, requirement_string):
         """DO NOT CALL THIS UNDOCUMENTED METHOD; use Requirement.parse()!"""
         super().__init__(requirement_string)
@@ -3353,6 +3347,6 @@ def _read_utf8_with_fallback(file: str, fallback_encoding=LOCALE_ENCODING) -> st
         """
         # TODO: Add a deadline?
         #       See comment in setuptools.unicode_utils._Utf8EncodingNeeded
-        warnings.warns(msg, PkgResourcesDeprecationWarning, stacklevel=2)
+        warnings.warn(msg, PkgResourcesDeprecationWarning, stacklevel=2)
         with open(file, "r", encoding=fallback_encoding) as f:
             return f.read()
diff --git a/pkg_resources/tests/test_resources.py b/pkg_resources/tests/test_resources.py
index b0a319e60f..83199af7b8 100644
--- a/pkg_resources/tests/test_resources.py
+++ b/pkg_resources/tests/test_resources.py
@@ -5,7 +5,7 @@
 import itertools
 
 import pytest
-from pkg_resources.extern import packaging
+from pkg_resources.extern.packaging.specifiers import SpecifierSet
 
 import pkg_resources
 from pkg_resources import (
@@ -567,7 +567,7 @@ def testOptionsAndHashing(self):
         assert hash(r1) == hash((
             "twisted",
             None,
-            packaging.specifiers.SpecifierSet(">=1.2"),
+            SpecifierSet(">=1.2"),
             frozenset(["foo", "bar"]),
             None,
         ))
@@ -576,7 +576,7 @@ def testOptionsAndHashing(self):
         ) == hash((
             "twisted",
             "https://localhost/twisted.zip",
-            packaging.specifiers.SpecifierSet(),
+            SpecifierSet(),
             frozenset(),
             None,
         ))

From 6c5da6cc6a6320331d4325a5d4501a3f36530d5d Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Thu, 9 May 2024 11:49:59 -0400
Subject: [PATCH 110/135] Apply suggestions from code review

Co-authored-by: Anderson Bravalheri 
---
 pkg_resources/__init__.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index b595ec5965..3a412a718c 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -74,7 +74,7 @@
 from pkg_resources.extern.packaging import requirements as _packaging_requirements
 from pkg_resources.extern.packaging import utils as _packaging_utils
 from pkg_resources.extern.packaging import version as _packaging_version
-from pkg_resources.extern.platformdirs import user_cache_dir
+from pkg_resources.extern.platformdirs import user_cache_dir as _user_cache_dir
 
 # declare some globals that will be defined later to
 # satisfy the linters.
@@ -1340,7 +1340,7 @@ def get_default_cache():
     or a platform-relevant user cache dir for an app
     named "Python-Eggs".
     """
-    return os.environ.get('PYTHON_EGG_CACHE') or user_cache_dir(appname='Python-Eggs')
+    return os.environ.get('PYTHON_EGG_CACHE') or _user_cache_dir(appname='Python-Eggs')
 
 
 def safe_name(name):

From 0802e21030ff750d30a48b2d77ce049af201e0ad Mon Sep 17 00:00:00 2001
From: James Gerity 
Date: Thu, 7 Dec 2023 21:08:18 -0500
Subject: [PATCH 111/135] Emit warning for invalid [tools.setuptools] table

---
 newsfragments/4150.feature.rst     |  1 +
 setuptools/config/pyprojecttoml.py | 10 ++++++++++
 2 files changed, 11 insertions(+)
 create mode 100644 newsfragments/4150.feature.rst

diff --git a/newsfragments/4150.feature.rst b/newsfragments/4150.feature.rst
new file mode 100644
index 0000000000..2521fbd805
--- /dev/null
+++ b/newsfragments/4150.feature.rst
@@ -0,0 +1 @@
+Emit a warning when ``[tools.setuptools]`` is present in ``pyproject.toml`` and will be ignored. -- by user:`SnoopJ`
diff --git a/setuptools/config/pyprojecttoml.py b/setuptools/config/pyprojecttoml.py
index ff97679895..d379405595 100644
--- a/setuptools/config/pyprojecttoml.py
+++ b/setuptools/config/pyprojecttoml.py
@@ -108,6 +108,10 @@ def read_configuration(
     if not asdict or not (project_table or setuptools_table):
         return {}  # User is not using pyproject to configure setuptools
 
+    if "setuptools" in asdict.get("tools", {}):
+        # let the user know they probably have a typo in their metadata
+        _ToolsTypoInMetadata.emit()
+
     if "distutils" in tool_table:
         _ExperimentalConfiguration.emit(subject="[tool.distutils]")
 
@@ -439,3 +443,9 @@ class _ExperimentalConfiguration(SetuptoolsWarning):
         "`{subject}` in `pyproject.toml` is still *experimental* "
         "and likely to change in future releases."
     )
+
+
+class _ToolsTypoInMetadata(SetuptoolsWarning):
+    _SUMMARY = (
+        "Ignoring [tools.setuptools] in pyproject.toml, did you mean [tool.setuptools]?"
+    )

From 8d1809d8c5df487d3296270bfda0737211354e82 Mon Sep 17 00:00:00 2001
From: James Gerity 
Date: Sat, 6 Apr 2024 20:07:18 -0400
Subject: [PATCH 112/135] Fix typo in test metadata

---
 setuptools/tests/test_build_py.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py
index db2052a586..c293fec142 100644
--- a/setuptools/tests/test_build_py.py
+++ b/setuptools/tests/test_build_py.py
@@ -349,7 +349,7 @@ class TestTypeInfoFiles:
             name = "foo"
             version = "1"
 
-            [tools.setuptools]
+            [tool.setuptools]
             include-package-data = false
             """
         ),
@@ -359,7 +359,7 @@ class TestTypeInfoFiles:
             name = "foo"
             version = "1"
 
-            [tools.setuptools]
+            [tool.setuptools]
             include-package-data = false
 
             [tool.setuptools.exclude-package-data]

From 26e9a71ff2d2314430effc2a4faa8b6b0c8a9d91 Mon Sep 17 00:00:00 2001
From: James Gerity 
Date: Sat, 6 Apr 2024 22:20:05 -0400
Subject: [PATCH 113/135] Add test for tools.setuptools warning

---
 setuptools/tests/config/test_pyprojecttoml.py | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/setuptools/tests/config/test_pyprojecttoml.py b/setuptools/tests/config/test_pyprojecttoml.py
index abec68ab30..4de11d8b1f 100644
--- a/setuptools/tests/config/test_pyprojecttoml.py
+++ b/setuptools/tests/config/test_pyprojecttoml.py
@@ -8,6 +8,7 @@
 from path import Path
 
 from setuptools.config.pyprojecttoml import (
+    _ToolsTypoInMetadata,
     read_configuration,
     expand_configuration,
     apply_configuration,
@@ -369,3 +370,28 @@ def test_include_package_data_in_setuppy(tmp_path):
     assert dist.get_name() == "myproj"
     assert dist.get_version() == "42"
     assert dist.include_package_data is False
+
+
+def test_warn_tools_typo(tmp_path):
+    """Test that the common ``tools.setuptools`` typo in ``pyproject.toml`` issues a warning
+
+    See https://github.com/pypa/setuptools/issues/4150
+    """
+    config = """
+    [build-system]
+    requires = ["setuptools"]
+    build-backend = "setuptools.build_meta"
+
+    [project]
+    name = "myproj"
+    version = '42'
+
+    [tools.setuptools]
+    packages = ["package"]
+    """
+
+    pyproject = tmp_path / "pyproject.toml"
+    pyproject.write_text(cleandoc(config), encoding="utf-8")
+
+    with pytest.warns(_ToolsTypoInMetadata):
+        expanded = read_configuration(pyproject)

From 2bfc431d197e49b5232639c7916887135ba29014 Mon Sep 17 00:00:00 2001
From: James Gerity 
Date: Sun, 7 Apr 2024 14:58:29 -0400
Subject: [PATCH 114/135] Remove unused variable

---
 setuptools/tests/config/test_pyprojecttoml.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/setuptools/tests/config/test_pyprojecttoml.py b/setuptools/tests/config/test_pyprojecttoml.py
index 4de11d8b1f..bf8cae5a24 100644
--- a/setuptools/tests/config/test_pyprojecttoml.py
+++ b/setuptools/tests/config/test_pyprojecttoml.py
@@ -394,4 +394,4 @@ def test_warn_tools_typo(tmp_path):
     pyproject.write_text(cleandoc(config), encoding="utf-8")
 
     with pytest.warns(_ToolsTypoInMetadata):
-        expanded = read_configuration(pyproject)
+        read_configuration(pyproject)

From 7bfee6ff5f26050472805ce3f9594a0e58cf20e8 Mon Sep 17 00:00:00 2001
From: James Gerity 
Date: Tue, 9 Apr 2024 12:49:11 -0400
Subject: [PATCH 115/135] Invoke build command in typing file test

Co-authored-by: Daniel Naylor 
---
 setuptools/tests/test_build_py.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py
index c293fec142..a1f595e27a 100644
--- a/setuptools/tests/test_build_py.py
+++ b/setuptools/tests/test_build_py.py
@@ -421,6 +421,7 @@ def test_type_files_included_by_default(self, tmpdir_cwd, pyproject, example):
         jaraco.path.build(structure)
 
         build_py = get_finalized_build_py()
+        build_py.run_command("build")
         outputs = get_outputs(build_py)
         assert expected_type_files <= outputs
 

From 9d86cfe905983a9c6b035bbd895a9cd42e06d64d Mon Sep 17 00:00:00 2001
From: James Gerity 
Date: Tue, 9 Apr 2024 18:50:17 -0400
Subject: [PATCH 116/135] Fix typo in NEWS fragment

---
 newsfragments/4150.feature.rst | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/newsfragments/4150.feature.rst b/newsfragments/4150.feature.rst
index 2521fbd805..5e536fd755 100644
--- a/newsfragments/4150.feature.rst
+++ b/newsfragments/4150.feature.rst
@@ -1 +1 @@
-Emit a warning when ``[tools.setuptools]`` is present in ``pyproject.toml`` and will be ignored. -- by user:`SnoopJ`
+Emit a warning when ``[tools.setuptools]`` is present in ``pyproject.toml`` and will be ignored. -- by :user:`SnoopJ`

From 1cbafacbdbdae7074285c46967338fcd67bc7d7c Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Thu, 9 May 2024 17:47:53 +0100
Subject: [PATCH 117/135] Use xfail to temporarily bypass failing test

---
 setuptools/tests/test_build_py.py | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/setuptools/tests/test_build_py.py b/setuptools/tests/test_build_py.py
index a1f595e27a..6900fdefbd 100644
--- a/setuptools/tests/test_build_py.py
+++ b/setuptools/tests/test_build_py.py
@@ -409,7 +409,14 @@ class TestTypeInfoFiles:
     }
 
     @pytest.mark.parametrize(
-        "pyproject", ["default_pyproject", "dont_include_package_data"]
+        "pyproject",
+        [
+            "default_pyproject",
+            pytest.param(
+                "dont_include_package_data",
+                marks=pytest.mark.xfail(reason="pypa/setuptools#4350"),
+            ),
+        ],
     )
     @pytest.mark.parametrize("example", EXAMPLES.keys())
     def test_type_files_included_by_default(self, tmpdir_cwd, pyproject, example):
@@ -421,7 +428,6 @@ def test_type_files_included_by_default(self, tmpdir_cwd, pyproject, example):
         jaraco.path.build(structure)
 
         build_py = get_finalized_build_py()
-        build_py.run_command("build")
         outputs = get_outputs(build_py)
         assert expected_type_files <= outputs
 

From 3d7599beb613f76afd54209fea18c8d569b04a39 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Thu, 9 May 2024 18:09:23 +0100
Subject: [PATCH 118/135] Address warning

---
 tools/vendored.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/vendored.py b/tools/vendored.py
index 7d4399bbdc..6257a6bf33 100644
--- a/tools/vendored.py
+++ b/tools/vendored.py
@@ -7,7 +7,7 @@
 
 def remove_all(paths):
     for path in paths:
-        path.rmtree() if path.isdir() else path.remove()
+        path.rmtree() if path.is_dir() else path.remove()
 
 
 def update_vendored():

From bb52e72388f5ec8008f1008b019bcca21d2782c1 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Thu, 9 May 2024 18:13:25 +0100
Subject: [PATCH 119/135] Improve auto-update of extern list

---
 pkg_resources/extern/__init__.py |  4 ++--
 setuptools/extern/__init__.py    |  4 ++--
 tools/vendored.py                | 27 ++++++++++++++++-----------
 3 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py
index 363bdf3f06..b6294dbfdb 100644
--- a/pkg_resources/extern/__init__.py
+++ b/pkg_resources/extern/__init__.py
@@ -72,8 +72,8 @@ def install(self):
 
 # [[[cog
 # import cog
-# from tools.vendored import yield_root_package
-# names = "\n".join(f"    {x!r}," for x in yield_root_package('pkg_resources'))
+# from tools.vendored import yield_top_level
+# names = "\n".join(f"    {x!r}," for x in yield_top_level('pkg_resources'))
 # cog.outl(f"names = (\n{names}\n)")
 # ]]]
 names = (
diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py
index 3e66269bec..b0e646bc8c 100644
--- a/setuptools/extern/__init__.py
+++ b/setuptools/extern/__init__.py
@@ -72,8 +72,8 @@ def install(self):
 
 # [[[cog
 # import cog
-# from tools.vendored import yield_root_package
-# names = "\n".join(f"    {x!r}," for x in yield_root_package('setuptools'))
+# from tools.vendored import yield_top_level
+# names = "\n".join(f"    {x!r}," for x in yield_top_level('setuptools'))
 # cog.outl(f"names = (\n{names}\n)")
 # ]]]
 names = (
diff --git a/tools/vendored.py b/tools/vendored.py
index 6257a6bf33..4c7b03ebce 100644
--- a/tools/vendored.py
+++ b/tools/vendored.py
@@ -165,18 +165,23 @@ def update_setuptools():
     rewrite_more_itertools(vendor / "more_itertools")
 
 
-def yield_root_package(name):
-    """Useful when defining the MetaPathFinder
-    >>> examples = set(yield_root_package("setuptools")) & {"jaraco", "backports"}
+def yield_top_level(name):
+    """Iterate over all modules and (top level) packages vendored
+    >>> roots = set(yield_top_level("setuptools"))
+    >>> examples = roots & {"jaraco", "backports", "zipp"}
     >>> list(sorted(examples))
-    ['backports', 'jaraco']
-    """
-    vendored = Path(f"{name}/_vendor/vendored.txt")
-    yield from (
-        line.partition("=")[0].partition(".")[0].replace("-", "_")
-        for line in vendored.read_text(encoding="utf-8").splitlines()
-        if line and not line.startswith("#")
-    )
+    ['backports', 'jaraco', 'zipp']
+    """
+    vendor = Path(f"{name}/_vendor")
+    ignore = {"__pycache__", "__init__.py", ".ruff_cache"}
+
+    for item in vendor.iterdir():
+        if item.name in ignore:
+            continue
+        if item.is_dir() and item.suffix != ".dist-info":
+            yield str(item.name)
+        if item.is_file() and item.suffix == ".py":
+            yield str(item.stem)
 
 
 __name__ == '__main__' and update_vendored()

From c279742ed6eb3696ee592322e5ae7b1eb4c640ab Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Fri, 10 May 2024 10:53:19 +0100
Subject: [PATCH 120/135] Reorder imports in vendor to match validation

---
 pkg_resources/extern/__init__.py |  8 ++++----
 setuptools/extern/__init__.py    | 14 +++++++-------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/pkg_resources/extern/__init__.py b/pkg_resources/extern/__init__.py
index b6294dbfdb..bfb9eb8bdf 100644
--- a/pkg_resources/extern/__init__.py
+++ b/pkg_resources/extern/__init__.py
@@ -77,13 +77,13 @@ def install(self):
 # cog.outl(f"names = (\n{names}\n)")
 # ]]]
 names = (
+    'backports',
+    'importlib_resources',
+    'jaraco',
+    'more_itertools',
     'packaging',
     'platformdirs',
-    'jaraco',
-    'importlib_resources',
     'zipp',
-    'more_itertools',
-    'backports',
 )
 # [[[end]]]
 VendorImporter(__name__, names).install()
diff --git a/setuptools/extern/__init__.py b/setuptools/extern/__init__.py
index b0e646bc8c..8eb02ac6d3 100644
--- a/setuptools/extern/__init__.py
+++ b/setuptools/extern/__init__.py
@@ -77,15 +77,15 @@ def install(self):
 # cog.outl(f"names = (\n{names}\n)")
 # ]]]
 names = (
-    'packaging',
-    'ordered_set',
-    'more_itertools',
-    'jaraco',
-    'importlib_resources',
+    'backports',
     'importlib_metadata',
-    'zipp',
+    'importlib_resources',
+    'jaraco',
+    'more_itertools',
+    'ordered_set',
+    'packaging',
     'tomli',
-    'backports',
+    'zipp',
 )
 # [[[end]]]
 VendorImporter(__name__, names, 'setuptools._vendor').install()

From 639b93d88c9764a29d127ec7b60251ac7f433d3e Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Fri, 10 May 2024 11:22:28 +0100
Subject: [PATCH 121/135] Show diff in check-extern for better debuging

---
 tox.ini | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tox.ini b/tox.ini
index e13ad53da3..e6fc063af1 100644
--- a/tox.ini
+++ b/tox.ini
@@ -70,14 +70,14 @@ commands =
 
 [testenv:{vendor,check-extern}]
 skip_install = True
-allowlist_externals = sh
+allowlist_externals = git, sh
 deps =
 	path
 	cogapp
 commands =
 	vendor: python -m tools.vendored
-	vendor: sh -c "git grep -l -F '\[\[\[cog' | xargs cog -I {toxinidir} -r"  # update `*.extern`
-	check-extern: sh -c "git grep -l -F '\[\[\[cog' | xargs cog -I {toxinidir} --check"
+	sh -c "git grep -l -F '\[\[\[cog' | xargs -t cog -I {toxinidir} -r"  # update `*.extern`
+	check-extern: git diff --exit-code
 
 [testenv:generate-validation-code]
 skip_install = True

From 3cfe932d88e21acc4c7c448089cb2c409a9f4491 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Fri, 10 May 2024 11:44:03 +0100
Subject: [PATCH 122/135] Sort items in yield_top_level for better
 reproducibility

---
 tools/vendored.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/vendored.py b/tools/vendored.py
index 4c7b03ebce..69a538f20c 100644
--- a/tools/vendored.py
+++ b/tools/vendored.py
@@ -175,7 +175,7 @@ def yield_top_level(name):
     vendor = Path(f"{name}/_vendor")
     ignore = {"__pycache__", "__init__.py", ".ruff_cache"}
 
-    for item in vendor.iterdir():
+    for item in sorted(vendor.iterdir()):
         if item.name in ignore:
             continue
         if item.is_dir() and item.suffix != ".dist-info":

From 544b332bd78d8d274597923e89b9bd7839f8a0f4 Mon Sep 17 00:00:00 2001
From: Avasam 
Date: Mon, 13 May 2024 02:27:49 -0400
Subject: [PATCH 123/135] `pkg_resources` type the declared global variables
 (#4267)

---
 newsfragments/4267.feature.rst |  1 +
 pkg_resources/__init__.py      | 64 +++++++++++++++++++++-------------
 2 files changed, 40 insertions(+), 25 deletions(-)
 create mode 100644 newsfragments/4267.feature.rst

diff --git a/newsfragments/4267.feature.rst b/newsfragments/4267.feature.rst
new file mode 100644
index 0000000000..5a69c23914
--- /dev/null
+++ b/newsfragments/4267.feature.rst
@@ -0,0 +1 @@
+Typed the dynamically defined variables from `pkg_resources` -- by :user:`Avasam`
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 3a412a718c..713d9bdfa3 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -27,7 +27,16 @@
 import time
 import re
 import types
-from typing import Callable, Dict, Iterable, List, Protocol, Optional, TypeVar
+from typing import (
+    TYPE_CHECKING,
+    List,
+    Protocol,
+    Callable,
+    Dict,
+    Iterable,
+    Optional,
+    TypeVar,
+)
 import zipfile
 import zipimport
 import warnings
@@ -76,21 +85,6 @@
 from pkg_resources.extern.packaging import version as _packaging_version
 from pkg_resources.extern.platformdirs import user_cache_dir as _user_cache_dir
 
-# declare some globals that will be defined later to
-# satisfy the linters.
-require = None
-working_set = None
-add_activation_listener = None
-cleanup_resources = None
-resource_stream = None
-set_extraction_path = None
-resource_isdir = None
-resource_string = None
-iter_entry_points = None
-resource_listdir = None
-resource_filename = None
-resource_exists = None
-
 
 warnings.warn(
     "pkg_resources is deprecated as an API. "
@@ -3257,6 +3251,15 @@ def _mkstemp(*args, **kw):
 warnings.filterwarnings("ignore", category=PEP440Warning, append=True)
 
 
+class PkgResourcesDeprecationWarning(Warning):
+    """
+    Base class for warning about deprecations in ``pkg_resources``
+
+    This class is not derived from ``DeprecationWarning``, and as such is
+    visible by default.
+    """
+
+
 # from jaraco.functools 1.3
 def _call_aside(f, *args, **kwargs):
     f(*args, **kwargs)
@@ -3275,15 +3278,6 @@ def _initialize(g=globals()):
     )
 
 
-class PkgResourcesDeprecationWarning(Warning):
-    """
-    Base class for warning about deprecations in ``pkg_resources``
-
-    This class is not derived from ``DeprecationWarning``, and as such is
-    visible by default.
-    """
-
-
 @_call_aside
 def _initialize_master_working_set():
     """
@@ -3320,6 +3314,26 @@ def _initialize_master_working_set():
     globals().update(locals())
 
 
+if TYPE_CHECKING:
+    # All of these are set by the @_call_aside methods above
+    __resource_manager = ResourceManager()  # Won't exist at runtime
+    resource_exists = __resource_manager.resource_exists
+    resource_isdir = __resource_manager.resource_isdir
+    resource_filename = __resource_manager.resource_filename
+    resource_stream = __resource_manager.resource_stream
+    resource_string = __resource_manager.resource_string
+    resource_listdir = __resource_manager.resource_listdir
+    set_extraction_path = __resource_manager.set_extraction_path
+    cleanup_resources = __resource_manager.cleanup_resources
+
+    working_set = WorkingSet()
+    require = working_set.require
+    iter_entry_points = working_set.iter_entry_points
+    add_activation_listener = working_set.subscribe
+    run_script = working_set.run_script
+    run_main = run_script
+
+
 #  ---- Ported from ``setuptools`` to avoid introducing an import inter-dependency ----
 LOCALE_ENCODING = "locale" if sys.version_info >= (3, 10) else None
 

From d53bf1509f40c8e84feb62ac13e91b76074a063a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= 
Date: Tue, 14 May 2024 16:19:02 +0200
Subject: [PATCH 124/135] Explicitly disallow resource paths starting with
 single backslash

Previously, such paths were disallowed implicitly
as they were treated as Windows absolute paths.

Since Python 3.13, paths starting with a single backslash are not considered
Windows-absolute, so we treat them specially.

This change makes the existing doctest pass with Python 3.13.

Partially fixes https://github.com/pypa/setuptools/issues/4196
---
 pkg_resources/__init__.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index 713d9bdfa3..faee7dec79 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -1604,6 +1604,7 @@ def _validate_resource_path(path):
             os.path.pardir in path.split(posixpath.sep)
             or posixpath.isabs(path)
             or ntpath.isabs(path)
+            or path.startswith("\\")
         )
         if not invalid:
             return
@@ -1611,7 +1612,7 @@ def _validate_resource_path(path):
         msg = "Use of .. or absolute path in a resource path is not allowed."
 
         # Aggressively disallow Windows absolute paths
-        if ntpath.isabs(path) and not posixpath.isabs(path):
+        if (path.startswith("\\") or ntpath.isabs(path)) and not posixpath.isabs(path):
             raise ValueError(msg)
 
         # for compatibility, warn; in future

From c6266e423fa26aafa01f1df71de7c6613273155e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= 
Date: Tue, 14 May 2024 16:24:07 +0200
Subject: [PATCH 125/135] Make the validation test for entry-points work with
 Python 3.13+

The exception in importlib.metadata has changed.
See https://github.com/python/importlib_metadata/issues/488

This makes an existing test pass with Python 3.13.

Partially fixes https://github.com/pypa/setuptools/issues/4196
---
 setuptools/_entry_points.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/setuptools/_entry_points.py b/setuptools/_entry_points.py
index 747a69067e..b244e78387 100644
--- a/setuptools/_entry_points.py
+++ b/setuptools/_entry_points.py
@@ -17,7 +17,8 @@ def ensure_valid(ep):
     """
     try:
         ep.extras
-    except AttributeError as ex:
+    except (AttributeError, AssertionError) as ex:
+        # Why both? See https://github.com/python/importlib_metadata/issues/488
         msg = (
             f"Problems to parse {ep}.\nPlease ensure entry-point follows the spec: "
             "https://packaging.python.org/en/latest/specifications/entry-points/"

From 06ee10669eb4d8c477d1b3eb2b3884ea93eb3ccd Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Wed, 15 May 2024 16:48:33 +0100
Subject: [PATCH 126/135] Remove deprecated `setuptools.dep_util`

Users should rely on `setuptools.modified` instead.
---
 setuptools/dep_util.py | 16 ----------------
 1 file changed, 16 deletions(-)
 delete mode 100644 setuptools/dep_util.py

diff --git a/setuptools/dep_util.py b/setuptools/dep_util.py
deleted file mode 100644
index 998ffa206e..0000000000
--- a/setuptools/dep_util.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from ._distutils import _modified
-from .warnings import SetuptoolsDeprecationWarning
-
-
-def __getattr__(name):
-    if name not in ['newer_group', 'newer_pairwise_group']:
-        raise AttributeError(name)
-    SetuptoolsDeprecationWarning.emit(
-        "dep_util is Deprecated. Use functions from setuptools.modified instead.",
-        "Please use `setuptools.modified` instead of `setuptools.dep_util`.",
-        see_url="https://github.com/pypa/setuptools/pull/4069",
-        due_date=(2024, 5, 21),
-        # Warning added in v69.0.0 on 2023/11/20,
-        # See https://github.com/pypa/setuptools/discussions/4128
-    )
-    return getattr(_modified, name)

From 126b730c0ff1cb01e2e2aa6bf8c1feee4a216c34 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Wed, 15 May 2024 17:18:16 +0100
Subject: [PATCH 127/135] Add news fragment

---
 newsfragments/4360.removal.1.rst | 2 ++
 1 file changed, 2 insertions(+)
 create mode 100644 newsfragments/4360.removal.1.rst

diff --git a/newsfragments/4360.removal.1.rst b/newsfragments/4360.removal.1.rst
new file mode 100644
index 0000000000..f00d6be518
--- /dev/null
+++ b/newsfragments/4360.removal.1.rst
@@ -0,0 +1,2 @@
+Remove deprecated ``setuptools.dep_util``.
+The provided alternative is ``setuptools.modified``.

From 59c47e42e304f0c56a9069b86bca2f11ca6ced7b Mon Sep 17 00:00:00 2001
From: "Jason R. Coombs" 
Date: Thu, 16 May 2024 21:30:08 -0400
Subject: [PATCH 128/135] In install command, use super to call the superclass
 methods. Avoids race conditions when monkeypatching from
 _distutils_system_mod occurs late.

Fixes #4136
---
 newsfragments/4136.bugfix.rst | 1 +
 setuptools/command/install.py | 8 ++++----
 2 files changed, 5 insertions(+), 4 deletions(-)
 create mode 100644 newsfragments/4136.bugfix.rst

diff --git a/newsfragments/4136.bugfix.rst b/newsfragments/4136.bugfix.rst
new file mode 100644
index 0000000000..f56346f0c7
--- /dev/null
+++ b/newsfragments/4136.bugfix.rst
@@ -0,0 +1 @@
+In install command, use super to call the superclass methods. Avoids race conditions when monkeypatching from _distutils_system_mod occurs late.
\ No newline at end of file
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index 56c1155b50..c49fcda939 100644
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -49,12 +49,12 @@ def initialize_options(self):
             #       and then add a due_date to this warning.
         )
 
-        orig.install.initialize_options(self)
+        super().initialize_options()
         self.old_and_unmanageable = None
         self.single_version_externally_managed = None
 
     def finalize_options(self):
-        orig.install.finalize_options(self)
+        super().finalize_options()
         if self.root:
             self.single_version_externally_managed = True
         elif self.single_version_externally_managed:
@@ -78,11 +78,11 @@ def handle_extra_path(self):
     def run(self):
         # Explicit request for old-style install?  Just do it
         if self.old_and_unmanageable or self.single_version_externally_managed:
-            return orig.install.run(self)
+            return super().run()
 
         if not self._called_from_setup(inspect.currentframe()):
             # Run in backward-compatibility mode to support bdist_* commands.
-            orig.install.run(self)
+            super().run()
         else:
             self.do_egg_install()
 

From 96d681aa405460f724c62c00ca125ae722ad810a Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Fri, 17 May 2024 10:43:17 +0100
Subject: [PATCH 129/135] Remove call to deprecated validate_pyproject command

---
 tools/generate_validation_code.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/generate_validation_code.py b/tools/generate_validation_code.py
index b575fb1e1c..0171325bd0 100644
--- a/tools/generate_validation_code.py
+++ b/tools/generate_validation_code.py
@@ -14,7 +14,7 @@ def generate_pyproject_validation(dest: Union[str, PathLike]):
     cmd = [
         sys.executable,
         "-m",
-        "validate_pyproject.vendoring",
+        "validate_pyproject.pre_compile",
         f"--output-dir={dest}",
         "--enable-plugins",
         "setuptools",

From 2a53cc1200ec4b14e08e84be3c042f8983dfb7d7 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Mon, 20 May 2024 09:19:50 +0100
Subject: [PATCH 130/135] Prevent 'bin' folders to be taken as extern packages

---
 tools/vendored.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/vendored.py b/tools/vendored.py
index 69a538f20c..57fd093aab 100644
--- a/tools/vendored.py
+++ b/tools/vendored.py
@@ -173,7 +173,7 @@ def yield_top_level(name):
     ['backports', 'jaraco', 'zipp']
     """
     vendor = Path(f"{name}/_vendor")
-    ignore = {"__pycache__", "__init__.py", ".ruff_cache"}
+    ignore = {"__pycache__", "__init__.py", ".ruff_cache", "bin"}
 
     for item in sorted(vendor.iterdir()):
         if item.name in ignore:

From 69141f69f8bf38da34cbea552d6fdaa9c8619c53 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Mon, 20 May 2024 09:36:27 +0100
Subject: [PATCH 131/135] Add doctest for vendorised bin folder

---
 tools/vendored.py | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/vendored.py b/tools/vendored.py
index 57fd093aab..41079e1330 100644
--- a/tools/vendored.py
+++ b/tools/vendored.py
@@ -171,6 +171,8 @@ def yield_top_level(name):
     >>> examples = roots & {"jaraco", "backports", "zipp"}
     >>> list(sorted(examples))
     ['backports', 'jaraco', 'zipp']
+    >>> 'bin' in examples
+    False
     """
     vendor = Path(f"{name}/_vendor")
     ignore = {"__pycache__", "__init__.py", ".ruff_cache", "bin"}

From d14fa0162c95450898c11534caf26a0f03553176 Mon Sep 17 00:00:00 2001
From: "Jason R. Coombs" 
Date: Mon, 20 May 2024 12:25:42 -0400
Subject: [PATCH 132/135] Add all site-packages dirs when creating simulated
 environment for test_editable_prefix.

Closes #4371.
---
 setuptools/tests/test_editable_install.py | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py
index 300a02cfb9..2fe096f0dd 100644
--- a/setuptools/tests/test_editable_install.py
+++ b/setuptools/tests/test_editable_install.py
@@ -408,17 +408,19 @@ def test_editable_with_prefix(tmp_path, sample_project, editable_opts):
     prefix = tmp_path / 'prefix'
 
     # figure out where pip will likely install the package
-    site_packages = prefix / next(
-        Path(path).relative_to(sys.prefix)
+    site_packages_all = [
+        prefix / Path(path).relative_to(sys.prefix)
         for path in sys.path
         if 'site-packages' in path and path.startswith(sys.prefix)
-    )
-    site_packages.mkdir(parents=True)
+    ]
+
+    for sp in site_packages_all:
+        sp.mkdir(parents=True)
 
     # install workaround
-    _addsitedir(site_packages)
+    _addsitedirs(site_packages_all)
 
-    env = dict(os.environ, PYTHONPATH=str(site_packages))
+    env = dict(os.environ, PYTHONPATH=os.pathsep.join(map(str, site_packages_all)))
     cmd = [
         sys.executable,
         '-m',
@@ -1250,14 +1252,17 @@ def install_project(name, venv, tmp_path, files, *opts):
     return project, out
 
 
-def _addsitedir(new_dir: Path):
+def _addsitedirs(new_dirs):
     """To use this function, it is necessary to insert new_dir in front of sys.path.
     The Python process will try to import a ``sitecustomize`` module on startup.
     If we manipulate sys.path/PYTHONPATH, we can force it to run our code,
     which invokes ``addsitedir`` and ensure ``.pth`` files are loaded.
     """
-    file = f"import site; site.addsitedir({os.fspath(new_dir)!r})\n"
-    (new_dir / "sitecustomize.py").write_text(file, encoding="utf-8")
+    content = '\n'.join(
+        ("import site",)
+        + tuple(f"site.addsitedir({os.fspath(new_dir)!r})" for new_dir in new_dirs)
+    )
+    (new_dirs[0] / "sitecustomize.py").write_text(content, encoding="utf-8")
 
 
 # ---- Assertion Helpers ----

From 6c1ef5748dbd70c8c5423e12680345766ee101d9 Mon Sep 17 00:00:00 2001
From: "Jason R. Coombs" 
Date: Mon, 20 May 2024 12:27:35 -0400
Subject: [PATCH 133/135] Remove xfail now that test passes. Ref #4371.

---
 setuptools/tests/test_editable_install.py | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/setuptools/tests/test_editable_install.py b/setuptools/tests/test_editable_install.py
index 2fe096f0dd..24c10a5054 100644
--- a/setuptools/tests/test_editable_install.py
+++ b/setuptools/tests/test_editable_install.py
@@ -396,11 +396,6 @@ def test_namespace_accidental_config_in_lenient_mode(self, venv, tmp_path):
         assert "mypkg.other not defined" in out
 
 
-# Moved here from test_develop:
-@pytest.mark.xfail(
-    platform.python_implementation() == 'PyPy',
-    reason="Workaround fails on PyPy (why?)",
-)
 def test_editable_with_prefix(tmp_path, sample_project, editable_opts):
     """
     Editable install to a prefix should be discoverable.

From 9c1bcc3417bd12668123f7e731e241d9e57bfc57 Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Tue, 21 May 2024 10:05:22 +0100
Subject: [PATCH 134/135] =?UTF-8?q?Bump=20version:=2069.5.1=20=E2=86=92=20?=
 =?UTF-8?q?70.0.0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .bumpversion.cfg                 |  2 +-
 NEWS.rst                         | 56 ++++++++++++++++++++++++++++++++
 newsfragments/4136.bugfix.rst    |  1 -
 newsfragments/4150.feature.rst   |  1 -
 newsfragments/4255.misc.rst      |  1 -
 newsfragments/4262.feature.rst   |  3 --
 newsfragments/4267.feature.rst   |  1 -
 newsfragments/4278.bugfix.rst    |  2 --
 newsfragments/4280.misc.rst      |  1 -
 newsfragments/4282.misc.rst      |  1 -
 newsfragments/4308.misc.rst      |  2 --
 newsfragments/4309.removal.rst   |  7 ----
 newsfragments/4312.doc.rst       |  1 -
 newsfragments/4322.removal.1.rst |  3 --
 newsfragments/4322.removal.2.rst |  3 --
 newsfragments/4324.removal.rst   |  1 -
 newsfragments/4332.feature.rst   |  1 -
 newsfragments/4348.bugfix.rst    |  1 -
 newsfragments/4348.misc.rst      |  1 -
 newsfragments/4360.removal.1.rst |  2 --
 setup.cfg                        |  2 +-
 21 files changed, 58 insertions(+), 35 deletions(-)
 delete mode 100644 newsfragments/4136.bugfix.rst
 delete mode 100644 newsfragments/4150.feature.rst
 delete mode 100644 newsfragments/4255.misc.rst
 delete mode 100644 newsfragments/4262.feature.rst
 delete mode 100644 newsfragments/4267.feature.rst
 delete mode 100644 newsfragments/4278.bugfix.rst
 delete mode 100644 newsfragments/4280.misc.rst
 delete mode 100644 newsfragments/4282.misc.rst
 delete mode 100644 newsfragments/4308.misc.rst
 delete mode 100644 newsfragments/4309.removal.rst
 delete mode 100644 newsfragments/4312.doc.rst
 delete mode 100644 newsfragments/4322.removal.1.rst
 delete mode 100644 newsfragments/4322.removal.2.rst
 delete mode 100644 newsfragments/4324.removal.rst
 delete mode 100644 newsfragments/4332.feature.rst
 delete mode 100644 newsfragments/4348.bugfix.rst
 delete mode 100644 newsfragments/4348.misc.rst
 delete mode 100644 newsfragments/4360.removal.1.rst

diff --git a/.bumpversion.cfg b/.bumpversion.cfg
index 557ae0ce34..70a34a3ed9 100644
--- a/.bumpversion.cfg
+++ b/.bumpversion.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 69.5.1
+current_version = 70.0.0
 commit = True
 tag = True
 
diff --git a/NEWS.rst b/NEWS.rst
index 73a8148d9c..06da16714b 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -1,3 +1,59 @@
+v70.0.0
+=======
+
+Features
+--------
+
+- Emit a warning when ``[tools.setuptools]`` is present in ``pyproject.toml`` and will be ignored. -- by :user:`SnoopJ` (#4150)
+- Improved `AttributeError` error message if ``pkg_resources.EntryPoint.require`` is called without extras or distribution
+  Gracefully "do nothing" when trying to activate a ``pkg_resources.Distribution`` with a `None` location, rather than raising a `TypeError`
+  -- by :user:`Avasam` (#4262)
+- Typed the dynamically defined variables from `pkg_resources` -- by :user:`Avasam` (#4267)
+- Modernized and refactored VCS handling in package_index. (#4332)
+
+
+Bugfixes
+--------
+
+- In install command, use super to call the superclass methods. Avoids race conditions when monkeypatching from _distutils_system_mod occurs late. (#4136)
+- Fix finder template for lenient editable installs of implicit nested namespaces
+  constructed by using ``package_dir`` to reorganise directory structure. (#4278)
+- Fix an error with `UnicodeDecodeError` handling in ``pkg_resources`` when trying to read files in UTF-8 with a fallback -- by :user:`Avasam` (#4348)
+
+
+Improved Documentation
+----------------------
+
+- Uses RST substitution to put badges in 1 line. (#4312)
+
+
+Deprecations and Removals
+-------------------------
+
+- Further adoption of UTF-8 in ``setuptools``.
+  This change regards mostly files produced and consumed during the build process
+  (e.g. metadata files, script wrappers, automatically updated config files, etc..)
+  Although precautions were taken to minimize disruptions, some edge cases might
+  be subject to backwards incompatibility.
+
+  Support for ``"locale"`` encoding is now **deprecated**. (#4309)
+- Remove ``setuptools.convert_path`` after long deprecation period.
+  This function was never defined by ``setuptools`` itself, but rather a
+  side-effect of an import for internal usage. (#4322)
+- Remove fallback for customisations of ``distutils``' ``build.sub_command`` after long
+  deprecated period.
+  Users are advised to import ``build`` directly from ``setuptools.command.build``. (#4322)
+- Removed ``typing_extensions`` from vendored dependencies -- by :user:`Avasam` (#4324)
+- Remove deprecated ``setuptools.dep_util``.
+  The provided alternative is ``setuptools.modified``. (#4360)
+
+
+Misc
+----
+
+- #4255, #4280, #4282, #4308, #4348
+
+
 v69.5.1
 =======
 
diff --git a/newsfragments/4136.bugfix.rst b/newsfragments/4136.bugfix.rst
deleted file mode 100644
index f56346f0c7..0000000000
--- a/newsfragments/4136.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-In install command, use super to call the superclass methods. Avoids race conditions when monkeypatching from _distutils_system_mod occurs late.
\ No newline at end of file
diff --git a/newsfragments/4150.feature.rst b/newsfragments/4150.feature.rst
deleted file mode 100644
index 5e536fd755..0000000000
--- a/newsfragments/4150.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-Emit a warning when ``[tools.setuptools]`` is present in ``pyproject.toml`` and will be ignored. -- by :user:`SnoopJ`
diff --git a/newsfragments/4255.misc.rst b/newsfragments/4255.misc.rst
deleted file mode 100644
index 50a0a3d195..0000000000
--- a/newsfragments/4255.misc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Treat ``EncodingWarning``s as errors in tests. -- by :user:`Avasam`
diff --git a/newsfragments/4262.feature.rst b/newsfragments/4262.feature.rst
deleted file mode 100644
index 7bbdba87d2..0000000000
--- a/newsfragments/4262.feature.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Improved `AttributeError` error message if ``pkg_resources.EntryPoint.require`` is called without extras or distribution
-Gracefully "do nothing" when trying to activate a ``pkg_resources.Distribution`` with a `None` location, rather than raising a `TypeError`
--- by :user:`Avasam`
diff --git a/newsfragments/4267.feature.rst b/newsfragments/4267.feature.rst
deleted file mode 100644
index 5a69c23914..0000000000
--- a/newsfragments/4267.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-Typed the dynamically defined variables from `pkg_resources` -- by :user:`Avasam`
diff --git a/newsfragments/4278.bugfix.rst b/newsfragments/4278.bugfix.rst
deleted file mode 100644
index 5e606cced8..0000000000
--- a/newsfragments/4278.bugfix.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-Fix finder template for lenient editable installs of implicit nested namespaces
-constructed by using ``package_dir`` to reorganise directory structure.
diff --git a/newsfragments/4280.misc.rst b/newsfragments/4280.misc.rst
deleted file mode 100644
index aff6a7ca1c..0000000000
--- a/newsfragments/4280.misc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Avoid leaking loop variable ``name`` in ``AbstractSandbox`` -- by :user:`Avasam`
diff --git a/newsfragments/4282.misc.rst b/newsfragments/4282.misc.rst
deleted file mode 100644
index 841d1b292c..0000000000
--- a/newsfragments/4282.misc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Removed the ``setuptools[testing-integration]`` in favor of ``setuptools[testing]`` -- by :user:`Avasam`
diff --git a/newsfragments/4308.misc.rst b/newsfragments/4308.misc.rst
deleted file mode 100644
index 6c43f6338e..0000000000
--- a/newsfragments/4308.misc.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-Fix ``setuptools_wheel`` fixture and avoid the recursive creation of
-``build/lib/build/lib/build/...`` directories in the project root during tests.
diff --git a/newsfragments/4309.removal.rst b/newsfragments/4309.removal.rst
deleted file mode 100644
index b69b17d45f..0000000000
--- a/newsfragments/4309.removal.rst
+++ /dev/null
@@ -1,7 +0,0 @@
-Further adoption of UTF-8 in ``setuptools``.
-This change regards mostly files produced and consumed during the build process
-(e.g. metadata files, script wrappers, automatically updated config files, etc..)
-Although precautions were taken to minimize disruptions, some edge cases might
-be subject to backwards incompatibility.
-
-Support for ``"locale"`` encoding is now **deprecated**.
diff --git a/newsfragments/4312.doc.rst b/newsfragments/4312.doc.rst
deleted file mode 100644
index 7ada954876..0000000000
--- a/newsfragments/4312.doc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Uses RST substitution to put badges in 1 line.
diff --git a/newsfragments/4322.removal.1.rst b/newsfragments/4322.removal.1.rst
deleted file mode 100644
index 33360172d5..0000000000
--- a/newsfragments/4322.removal.1.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Remove ``setuptools.convert_path`` after long deprecation period.
-This function was never defined by ``setuptools`` itself, but rather a
-side-effect of an import for internal usage.
diff --git a/newsfragments/4322.removal.2.rst b/newsfragments/4322.removal.2.rst
deleted file mode 100644
index 88380f4c8d..0000000000
--- a/newsfragments/4322.removal.2.rst
+++ /dev/null
@@ -1,3 +0,0 @@
-Remove fallback for customisations of ``distutils``' ``build.sub_command`` after long
-deprecated period.
-Users are advised to import ``build`` directly from ``setuptools.command.build``.
diff --git a/newsfragments/4324.removal.rst b/newsfragments/4324.removal.rst
deleted file mode 100644
index 3782a0b81b..0000000000
--- a/newsfragments/4324.removal.rst
+++ /dev/null
@@ -1 +0,0 @@
-Removed ``typing_extensions`` from vendored dependencies -- by :user:`Avasam`
diff --git a/newsfragments/4332.feature.rst b/newsfragments/4332.feature.rst
deleted file mode 100644
index 9f46298adc..0000000000
--- a/newsfragments/4332.feature.rst
+++ /dev/null
@@ -1 +0,0 @@
-Modernized and refactored VCS handling in package_index.
\ No newline at end of file
diff --git a/newsfragments/4348.bugfix.rst b/newsfragments/4348.bugfix.rst
deleted file mode 100644
index a8bb79a123..0000000000
--- a/newsfragments/4348.bugfix.rst
+++ /dev/null
@@ -1 +0,0 @@
-Fix an error with `UnicodeDecodeError` handling in ``pkg_resources`` when trying to read files in UTF-8 with a fallback -- by :user:`Avasam`
diff --git a/newsfragments/4348.misc.rst b/newsfragments/4348.misc.rst
deleted file mode 100644
index 989226c4b3..0000000000
--- a/newsfragments/4348.misc.rst
+++ /dev/null
@@ -1 +0,0 @@
-Update dynamic module imports in ``pkg_resources`` to private alias static imports. Enabled ``attr-defined`` checks in mypy for ``pkg_resources`` -- by :user:`Avasam`
diff --git a/newsfragments/4360.removal.1.rst b/newsfragments/4360.removal.1.rst
deleted file mode 100644
index f00d6be518..0000000000
--- a/newsfragments/4360.removal.1.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-Remove deprecated ``setuptools.dep_util``.
-The provided alternative is ``setuptools.modified``.
diff --git a/setup.cfg b/setup.cfg
index 0756fa92ea..baed6f84ae 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = setuptools
-version = 69.5.1
+version = 70.0.0
 author = Python Packaging Authority
 author_email = distutils-sig@python.org
 description = Easily download, build, install, upgrade, and uninstall Python packages

From 5cbf12a9b63fd37985a4525617b46576b8ac3a7b Mon Sep 17 00:00:00 2001
From: Anderson Bravalheri 
Date: Tue, 21 May 2024 11:08:23 +0100
Subject: [PATCH 135/135] Workaround for release error in v70

---
 tox.ini | 1 +
 1 file changed, 1 insertion(+)

diff --git a/tox.ini b/tox.ini
index e6fc063af1..c6d7068907 100644
--- a/tox.ini
+++ b/tox.ini
@@ -91,6 +91,7 @@ description = publish the package to PyPI and GitHub
 skip_install = True
 deps =
 	build
+	pyproject-hooks!=1.1 # workaround for pypa/setuptools#4333
 	twine>=3
 	jaraco.develop>=7.1
 pass_env =