summaryrefslogtreecommitdiffstats
path: root/contrib/python/setuptools
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2026-01-12 15:32:30 +0300
committerrobot-piglet <[email protected]>2026-01-12 16:24:02 +0300
commitdc54dc0e114169a45d258090e01d908d0e840230 (patch)
tree0365e26395e5867e7caf4b4e87ca4c002a83d1ee /contrib/python/setuptools
parentfb5a1356f2dd34113f0068abf765ccbd66d81b2b (diff)
Intermediate changes
commit_hash:75a0732dfa2159d2a850910fcc47775956b989c8
Diffstat (limited to 'contrib/python/setuptools')
-rw-r--r--contrib/python/setuptools/py3/.dist-info/METADATA8
-rw-r--r--contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_core_metadata.py26
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/_modified.py30
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py606
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py66
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py1279
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/cmd.py174
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py27
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py11
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build.py19
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py25
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py54
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py19
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py17
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/check.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/config.py13
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install.py66
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py7
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install_headers.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py18
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py7
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py38
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compat/__init__.py7
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compat/numpy.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/__init__.py0
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py1394
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py340
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/errors.py24
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py614
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/unix.py422
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py230
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compilers/__init__.py0
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py348
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/dist.py231
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/errors.py38
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/extension.py35
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/filelist.py95
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/spawn.py27
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py49
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py405
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/util.py74
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/zosccompiler.py230
-rw-r--r--contrib/python/setuptools/py3/setuptools/_normalization.py29
-rw-r--r--contrib/python/setuptools/py3/setuptools/_static.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/build_meta.py62
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py8
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/editable_wheel.py6
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/install.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py60
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE2
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/extra_validations.py32
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py351
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py33
-rw-r--r--contrib/python/setuptools/py3/setuptools/dist.py219
-rw-r--r--contrib/python/setuptools/py3/setuptools/msvc.py15
-rw-r--r--contrib/python/setuptools/py3/setuptools/package_index.py64
-rw-r--r--contrib/python/setuptools/py3/setuptools/unicode_utils.py2
-rw-r--r--contrib/python/setuptools/py3/ya.make11
61 files changed, 4493 insertions, 3495 deletions
diff --git a/contrib/python/setuptools/py3/.dist-info/METADATA b/contrib/python/setuptools/py3/.dist-info/METADATA
index 2b81ee9bd49..8d42d121a54 100644
--- a/contrib/python/setuptools/py3/.dist-info/METADATA
+++ b/contrib/python/setuptools/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
-Metadata-Version: 2.2
+Metadata-Version: 2.4
Name: setuptools
-Version: 75.8.2
+Version: 79.0.1
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Author-email: Python Packaging Authority <[email protected]>
Project-URL: Source, https://github.com/pypa/setuptools
@@ -9,7 +9,6 @@ Project-URL: Changelog, https://setuptools.pypa.io/en/stable/history.html
Keywords: CPAN PyPI distutils eggs package management
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Libraries :: Python Modules
@@ -64,9 +63,7 @@ Requires-Dist: importlib_metadata>=6; python_version < "3.10" and extra == "core
Requires-Dist: tomli>=2.0.1; python_version < "3.11" and extra == "core"
Requires-Dist: wheel>=0.43.0; extra == "core"
Requires-Dist: platformdirs>=4.2.2; extra == "core"
-Requires-Dist: jaraco.collections; extra == "core"
Requires-Dist: jaraco.functools>=4; extra == "core"
-Requires-Dist: packaging; extra == "core"
Requires-Dist: more_itertools; extra == "core"
Provides-Extra: check
Requires-Dist: pytest-checkdocs>=2.4; extra == "check"
@@ -81,6 +78,7 @@ Requires-Dist: pytest-mypy; extra == "type"
Requires-Dist: mypy==1.14.*; extra == "type"
Requires-Dist: importlib_metadata>=7.0.2; python_version < "3.10" and extra == "type"
Requires-Dist: jaraco.develop>=7.21; sys_platform != "cygwin" and extra == "type"
+Dynamic: license-file
.. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg
:target: https://pypi.org/project/setuptools
diff --git a/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml b/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml
index 946363d50fb..5b5a5a7c61d 100644
--- a/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml
+++ b/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml
@@ -14,3 +14,6 @@ mark_as_sources:
- setuptools/command/test.py
exclude:
- setuptools/_vendor/*
+keep:
+ - setuptools/_distutils/compilers/__init__.py
+ - setuptools/_distutils/compilers/C/__init__.py
diff --git a/contrib/python/setuptools/py3/setuptools/_core_metadata.py b/contrib/python/setuptools/py3/setuptools/_core_metadata.py
index 850cc409f71..a52d5cf755c 100644
--- a/contrib/python/setuptools/py3/setuptools/_core_metadata.py
+++ b/contrib/python/setuptools/py3/setuptools/_core_metadata.py
@@ -28,7 +28,7 @@ from distutils.util import rfc822_escape
def get_metadata_version(self):
mv = getattr(self, 'metadata_version', None)
if mv is None:
- mv = Version('2.2')
+ mv = Version('2.4')
self.metadata_version = mv
return mv
@@ -88,6 +88,7 @@ def read_pkg_file(self, file):
self.url = _read_field_from_msg(msg, 'home-page')
self.download_url = _read_field_from_msg(msg, 'download-url')
self.license = _read_field_unescaped_from_msg(msg, 'license')
+ self.license_expression = _read_field_unescaped_from_msg(msg, 'license-expression')
self.long_description = _read_field_unescaped_from_msg(msg, 'description')
if self.long_description is None and self.metadata_version >= Version('2.1'):
@@ -175,8 +176,9 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
if attr_val is not None:
write_field(field, attr_val)
- license = self.get_license()
- if license:
+ if license_expression := self.license_expression:
+ write_field('License-Expression', license_expression)
+ elif license := self.get_license():
write_field('License', rfc822_escape(license))
for label, url in self.project_urls.items():
@@ -205,7 +207,8 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
if self.long_description_content_type:
write_field('Description-Content-Type', self.long_description_content_type)
- self._write_list(file, 'License-File', self.license_files or [])
+ safe_license_files = map(_safe_license_file, self.license_files or [])
+ self._write_list(file, 'License-File', safe_license_files)
_write_requirements(self, file)
for field, attr in _POSSIBLE_DYNAMIC_FIELDS.items():
@@ -291,6 +294,14 @@ def _distribution_fullname(name: str, version: str) -> str:
)
+def _safe_license_file(file):
+ # XXX: Do we need this after the deprecation discussed in #4892, #4896??
+ normalized = os.path.normpath(file).replace(os.sep, "/")
+ if "../" in normalized:
+ return os.path.basename(normalized) # Temporarily restore pre PEP639 behaviour
+ return normalized
+
+
_POSSIBLE_DYNAMIC_FIELDS = {
# Core Metadata Field x related Distribution attribute
"author": "author",
@@ -302,7 +313,12 @@ _POSSIBLE_DYNAMIC_FIELDS = {
"home-page": "url",
"keywords": "keywords",
"license": "license",
- # "license-file": "license_files", # XXX: does PEP 639 exempt Dynamic ??
+ # XXX: License-File is complicated because the user gives globs that are expanded
+ # during the build. Without special handling it is likely always
+ # marked as Dynamic, which is an acceptable outcome according to:
+ # https://github.com/pypa/setuptools/issues/4629#issuecomment-2331233677
+ "license-file": "license_files",
+ "license-expression": "license_expression", # PEP 639
"maintainer": "maintainer",
"maintainer-email": "maintainer_email",
"obsoletes": "obsoletes",
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py b/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py
index 7cdca9398f4..f64cab7d61a 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py
@@ -1,13 +1,24 @@
"""Timestamp comparison of files and groups of files."""
+from __future__ import annotations
+
import functools
import os.path
+from collections.abc import Callable, Iterable
+from typing import Literal, TypeVar
from jaraco.functools import splat
from .compat.py39 import zip_strict
from .errors import DistutilsFileError
+_SourcesT = TypeVar(
+ "_SourcesT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
+)
+_TargetsT = TypeVar(
+ "_TargetsT", bound="str | bytes | os.PathLike[str] | os.PathLike[bytes]"
+)
+
def _newer(source, target):
return not os.path.exists(target) or (
@@ -15,7 +26,10 @@ def _newer(source, target):
)
-def newer(source, target):
+def newer(
+ source: str | bytes | os.PathLike[str] | os.PathLike[bytes],
+ target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
+) -> bool:
"""
Is source modified more recently than target.
@@ -25,12 +39,16 @@ def newer(source, target):
Raises DistutilsFileError if 'source' does not exist.
"""
if not os.path.exists(source):
- raise DistutilsFileError(f"file '{os.path.abspath(source)}' does not exist")
+ raise DistutilsFileError(f"file {os.path.abspath(source)!r} does not exist")
return _newer(source, target)
-def newer_pairwise(sources, targets, newer=newer):
+def newer_pairwise(
+ sources: Iterable[_SourcesT],
+ targets: Iterable[_TargetsT],
+ newer: Callable[[_SourcesT, _TargetsT], bool] = newer,
+) -> tuple[list[_SourcesT], list[_TargetsT]]:
"""
Filter filenames where sources are newer than targets.
@@ -43,7 +61,11 @@ def newer_pairwise(sources, targets, newer=newer):
return tuple(map(list, zip(*newer_pairs))) or ([], [])
-def newer_group(sources, target, missing='error'):
+def newer_group(
+ sources: Iterable[str | bytes | os.PathLike[str] | os.PathLike[bytes]],
+ target: str | bytes | os.PathLike[str] | os.PathLike[bytes],
+ missing: Literal["error", "ignore", "newer"] = "error",
+) -> bool:
"""
Is target out-of-date with respect to any file in sources.
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py
index 97b067c6861..d07c86ef8e4 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py
@@ -1,604 +1,16 @@
-"""distutils._msvccompiler
-
-Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for Microsoft Visual Studio 2015.
-
-This module requires VS 2015 or later.
-"""
-
-# Written by Perry Stoll
-# hacked by Robin Becker and Thomas Heller to do a better job of
-# finding DevStudio (through the registry)
-# ported to VS 2005 and VS 2008 by Christian Heimes
-# ported to VS 2015 by Steve Dower
-
-import contextlib
-import os
-import subprocess
-import unittest.mock as mock
import warnings
-with contextlib.suppress(ImportError):
- import winreg
-
-from itertools import count
-
-from ._log import log
-from .ccompiler import CCompiler, gen_lib_options
-from .errors import (
- CompileError,
- DistutilsExecError,
- DistutilsPlatformError,
- LibError,
- LinkError,
-)
-from .util import get_host_platform, get_platform
-
-
-def _find_vc2015():
- try:
- key = winreg.OpenKeyEx(
- winreg.HKEY_LOCAL_MACHINE,
- r"Software\Microsoft\VisualStudio\SxS\VC7",
- access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY,
- )
- except OSError:
- log.debug("Visual C++ is not registered")
- return None, None
-
- best_version = 0
- best_dir = None
- with key:
- for i in count():
- try:
- v, vc_dir, vt = winreg.EnumValue(key, i)
- except OSError:
- break
- if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
- try:
- version = int(float(v))
- except (ValueError, TypeError):
- continue
- if version >= 14 and version > best_version:
- best_version, best_dir = version, vc_dir
- return best_version, best_dir
-
-
-def _find_vc2017():
- """Returns "15, path" based on the result of invoking vswhere.exe
- If no install is found, returns "None, None"
-
- The version is returned to avoid unnecessarily changing the function
- result. It may be ignored when the path is not None.
-
- If vswhere.exe is not available, by definition, VS 2017 is not
- installed.
- """
- root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
- if not root:
- return None, None
-
- variant = 'arm64' if get_platform() == 'win-arm64' else 'x86.x64'
- suitable_components = (
- f"Microsoft.VisualStudio.Component.VC.Tools.{variant}",
- "Microsoft.VisualStudio.Workload.WDExpress",
- )
-
- for component in suitable_components:
- # Workaround for `-requiresAny` (only available on VS 2017 > 15.6)
- with contextlib.suppress(
- subprocess.CalledProcessError, OSError, UnicodeDecodeError
- ):
- path = (
- subprocess.check_output([
- os.path.join(
- root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
- ),
- "-latest",
- "-prerelease",
- "-requires",
- component,
- "-property",
- "installationPath",
- "-products",
- "*",
- ])
- .decode(encoding="mbcs", errors="strict")
- .strip()
- )
-
- path = os.path.join(path, "VC", "Auxiliary", "Build")
- if os.path.isdir(path):
- return 15, path
-
- return None, None # no suitable component found
-
-
-PLAT_SPEC_TO_RUNTIME = {
- 'x86': 'x86',
- 'x86_amd64': 'x64',
- 'x86_arm': 'arm',
- 'x86_arm64': 'arm64',
-}
-
-
-def _find_vcvarsall(plat_spec):
- # bpo-38597: Removed vcruntime return value
- _, best_dir = _find_vc2017()
-
- if not best_dir:
- best_version, best_dir = _find_vc2015()
-
- if not best_dir:
- log.debug("No suitable Visual C++ version found")
- return None, None
-
- vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
- if not os.path.isfile(vcvarsall):
- log.debug("%s cannot be found", vcvarsall)
- return None, None
-
- return vcvarsall, None
-
-
-def _get_vc_env(plat_spec):
- if os.getenv("DISTUTILS_USE_SDK"):
- return {key.lower(): value for key, value in os.environ.items()}
-
- vcvarsall, _ = _find_vcvarsall(plat_spec)
- if not vcvarsall:
- raise DistutilsPlatformError(
- 'Microsoft Visual C++ 14.0 or greater is required. '
- 'Get it with "Microsoft C++ Build Tools": '
- 'https://visualstudio.microsoft.com/visual-cpp-build-tools/'
- )
-
- try:
- out = subprocess.check_output(
- f'cmd /u /c "{vcvarsall}" {plat_spec} && set',
- stderr=subprocess.STDOUT,
- ).decode('utf-16le', errors='replace')
- except subprocess.CalledProcessError as exc:
- log.error(exc.output)
- raise DistutilsPlatformError(f"Error executing {exc.cmd}")
-
- env = {
- key.lower(): value
- for key, _, value in (line.partition('=') for line in out.splitlines())
- if key and value
- }
-
- return env
-
-
-def _find_exe(exe, paths=None):
- """Return path to an MSVC executable program.
-
- Tries to find the program in several places: first, one of the
- MSVC program search paths from the registry; next, the directories
- in the PATH environment variable. If any of those work, return an
- absolute path that is known to exist. If none of them work, just
- return the original program name, 'exe'.
- """
- if not paths:
- paths = os.getenv('path').split(os.pathsep)
- for p in paths:
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
- return exe
-
-
-_vcvars_names = {
- 'win32': 'x86',
- 'win-amd64': 'amd64',
- 'win-arm32': 'arm',
- 'win-arm64': 'arm64',
-}
-
-
-def _get_vcvars_spec(host_platform, platform):
- """
- Given a host platform and platform, determine the spec for vcvarsall.
-
- Uses the native MSVC host if the host platform would need expensive
- emulation for x86.
-
- >>> _get_vcvars_spec('win-arm64', 'win32')
- 'arm64_x86'
- >>> _get_vcvars_spec('win-arm64', 'win-amd64')
- 'arm64_amd64'
-
- Otherwise, always cross-compile from x86 to work with the
- lighter-weight MSVC installs that do not include native 64-bit tools.
-
- >>> _get_vcvars_spec('win32', 'win32')
- 'x86'
- >>> _get_vcvars_spec('win-arm32', 'win-arm32')
- 'x86_arm'
- >>> _get_vcvars_spec('win-amd64', 'win-arm64')
- 'x86_arm64'
- """
- if host_platform != 'win-arm64':
- host_platform = 'win32'
- vc_hp = _vcvars_names[host_platform]
- vc_plat = _vcvars_names[platform]
- return vc_hp if vc_hp == vc_plat else f'{vc_hp}_{vc_plat}'
-
+from .compilers.C import msvc
-class MSVCCompiler(CCompiler):
- """Concrete class that implements an interface to Microsoft Visual C++,
- as defined by the CCompiler abstract class."""
+__all__ = ["MSVCCompiler"]
- compiler_type = 'msvc'
+MSVCCompiler = msvc.Compiler
- # Just set this so CCompiler's constructor doesn't barf. We currently
- # don't use the 'set_executables()' bureaucracy provided by CCompiler,
- # as it really isn't necessary for this sort of single-compiler class.
- # Would be nice to have a consistent interface with UnixCCompiler,
- # though, so it's worth thinking about.
- executables = {}
- # Private class data (need to distinguish C from C++ source for compiler)
- _c_extensions = ['.c']
- _cpp_extensions = ['.cc', '.cpp', '.cxx']
- _rc_extensions = ['.rc']
- _mc_extensions = ['.mc']
-
- # Needed for the filename generation methods provided by the
- # base class, CCompiler.
- src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
- res_extension = '.res'
- obj_extension = '.obj'
- static_lib_extension = '.lib'
- shared_lib_extension = '.dll'
- static_lib_format = shared_lib_format = '%s%s'
- exe_extension = '.exe'
-
- def __init__(self, verbose=False, dry_run=False, force=False):
- super().__init__(verbose, dry_run, force)
- # target platform (.plat_name is consistent with 'bdist')
- self.plat_name = None
- self.initialized = False
-
- @classmethod
- def _configure(cls, vc_env):
- """
- Set class-level include/lib dirs.
- """
- cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
- cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
-
- @staticmethod
- def _parse_path(val):
- return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
-
- def initialize(self, plat_name=None):
- # multi-init means we would need to check platform same each time...
- assert not self.initialized, "don't init multiple times"
- if plat_name is None:
- plat_name = get_platform()
- # sanity check for platforms to prevent obscure errors later.
- if plat_name not in _vcvars_names:
- raise DistutilsPlatformError(
- f"--plat-name must be one of {tuple(_vcvars_names)}"
- )
-
- plat_spec = _get_vcvars_spec(get_host_platform(), plat_name)
-
- vc_env = _get_vc_env(plat_spec)
- if not vc_env:
- raise DistutilsPlatformError(
- "Unable to find a compatible Visual Studio installation."
- )
- self._configure(vc_env)
-
- self._paths = vc_env.get('path', '')
- paths = self._paths.split(os.pathsep)
- self.cc = _find_exe("cl.exe", paths)
- self.linker = _find_exe("link.exe", paths)
- self.lib = _find_exe("lib.exe", paths)
- self.rc = _find_exe("rc.exe", paths) # resource compiler
- self.mc = _find_exe("mc.exe", paths) # message compiler
- self.mt = _find_exe("mt.exe", paths) # message compiler
-
- self.preprocess_options = None
- # bpo-38597: Always compile with dynamic linking
- # Future releases of Python 3.x will include all past
- # versions of vcruntime*.dll for compatibility.
- self.compile_options = ['/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD']
-
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/Zi',
- '/W3',
- '/D_DEBUG',
- ]
-
- ldflags = ['/nologo', '/INCREMENTAL:NO', '/LTCG']
-
- ldflags_debug = ['/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL']
-
- self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
- self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
- self.ldflags_shared = [
- *ldflags,
- '/DLL',
- '/MANIFEST:EMBED,ID=2',
- '/MANIFESTUAC:NO',
- ]
- self.ldflags_shared_debug = [
- *ldflags_debug,
- '/DLL',
- '/MANIFEST:EMBED,ID=2',
- '/MANIFESTUAC:NO',
- ]
- self.ldflags_static = [*ldflags]
- self.ldflags_static_debug = [*ldflags_debug]
-
- self._ldflags = {
- (CCompiler.EXECUTABLE, None): self.ldflags_exe,
- (CCompiler.EXECUTABLE, False): self.ldflags_exe,
- (CCompiler.EXECUTABLE, True): self.ldflags_exe_debug,
- (CCompiler.SHARED_OBJECT, None): self.ldflags_shared,
- (CCompiler.SHARED_OBJECT, False): self.ldflags_shared,
- (CCompiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
- (CCompiler.SHARED_LIBRARY, None): self.ldflags_static,
- (CCompiler.SHARED_LIBRARY, False): self.ldflags_static,
- (CCompiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
- }
-
- self.initialized = True
-
- # -- Worker methods ------------------------------------------------
-
- @property
- def out_extensions(self):
- return {
- **super().out_extensions,
- **{
- ext: self.res_extension
- for ext in self._rc_extensions + self._mc_extensions
- },
- }
-
- def compile( # noqa: C901
- self,
- sources,
- output_dir=None,
- macros=None,
- include_dirs=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- depends=None,
- ):
- if not self.initialized:
- self.initialize()
- compile_info = self._setup_compile(
- output_dir, macros, include_dirs, sources, depends, extra_postargs
+def __getattr__(name):
+ if name == '_get_vc_env':
+ warnings.warn(
+ "_get_vc_env is private; find an alternative (pypa/distutils#340)"
)
- macros, objects, extra_postargs, pp_opts, build = compile_info
-
- compile_opts = extra_preargs or []
- compile_opts.append('/c')
- if debug:
- compile_opts.extend(self.compile_options_debug)
- else:
- compile_opts.extend(self.compile_options)
-
- add_cpp_opts = False
-
- for obj in objects:
- try:
- src, ext = build[obj]
- except KeyError:
- continue
- if debug:
- # pass the full pathname to MSVC in debug mode,
- # this allows the debugger to find the source file
- # without asking the user to browse for it
- src = os.path.abspath(src)
-
- if ext in self._c_extensions:
- input_opt = "/Tc" + src
- elif ext in self._cpp_extensions:
- input_opt = "/Tp" + src
- add_cpp_opts = True
- elif ext in self._rc_extensions:
- # compile .RC to .RES file
- input_opt = src
- output_opt = "/fo" + obj
- try:
- self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- elif ext in self._mc_extensions:
- # Compile .MC to .RC file to .RES file.
- # * '-h dir' specifies the directory for the
- # generated include file
- # * '-r dir' specifies the target directory of the
- # generated RC file and the binary message resource
- # it includes
- #
- # For now (since there are no options to change this),
- # we use the source-directory for the include file and
- # the build directory for the RC file and message
- # resources. This works at least for win32all.
- h_dir = os.path.dirname(src)
- rc_dir = os.path.dirname(obj)
- try:
- # first compile .MC to .RC and .H file
- self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
- base, _ = os.path.splitext(os.path.basename(src))
- rc_file = os.path.join(rc_dir, base + '.rc')
- # then compile .RC to .RES file
- self.spawn([self.rc, "/fo" + obj, rc_file])
-
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- else:
- # how to handle this file?
- raise CompileError(f"Don't know how to compile {src} to {obj}")
-
- args = [self.cc] + compile_opts + pp_opts
- if add_cpp_opts:
- args.append('/EHsc')
- args.extend((input_opt, "/Fo" + obj))
- args.extend(extra_postargs)
-
- try:
- self.spawn(args)
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- return objects
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=False, target_lang=None
- ):
- if not self.initialized:
- self.initialize()
- objects, output_dir = self._fix_object_args(objects, output_dir)
- output_filename = self.library_filename(output_libname, output_dir=output_dir)
-
- if self._need_link(objects, output_filename):
- lib_args = objects + ['/OUT:' + output_filename]
- if debug:
- pass # XXX what goes here?
- try:
- log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
- self.spawn([self.lib] + lib_args)
- except DistutilsExecError as msg:
- raise LibError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def link(
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- if not self.initialized:
- self.initialize()
- objects, output_dir = self._fix_object_args(objects, output_dir)
- fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
- libraries, library_dirs, runtime_library_dirs = fixed_args
-
- if runtime_library_dirs:
- self.warn(
- "I don't know what to do with 'runtime_library_dirs': "
- + str(runtime_library_dirs)
- )
-
- lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
- if output_dir is not None:
- output_filename = os.path.join(output_dir, output_filename)
-
- if self._need_link(objects, output_filename):
- ldflags = self._ldflags[target_desc, debug]
-
- export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
-
- ld_args = (
- ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
- )
-
- # The MSVC linker generates .lib and .exp files, which cannot be
- # suppressed by any linker switches. The .lib files may even be
- # needed! Make sure they are generated in the temporary build
- # directory. Since they have different names for debug and release
- # builds, they can go into the same directory.
- build_temp = os.path.dirname(objects[0])
- if export_symbols is not None:
- (dll_name, dll_ext) = os.path.splitext(
- os.path.basename(output_filename)
- )
- implib_file = os.path.join(build_temp, self.library_filename(dll_name))
- ld_args.append('/IMPLIB:' + implib_file)
-
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend(extra_postargs)
-
- output_dir = os.path.dirname(os.path.abspath(output_filename))
- self.mkpath(output_dir)
- try:
- log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
- self.spawn([self.linker] + ld_args)
- except DistutilsExecError as msg:
- raise LinkError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def spawn(self, cmd):
- env = dict(os.environ, PATH=self._paths)
- with self._fallback_spawn(cmd, env) as fallback:
- return super().spawn(cmd, env=env)
- return fallback.value
-
- @contextlib.contextmanager
- def _fallback_spawn(self, cmd, env):
- """
- Discovered in pypa/distutils#15, some tools monkeypatch the compiler,
- so the 'env' kwarg causes a TypeError. Detect this condition and
- restore the legacy, unsafe behavior.
- """
- bag = type('Bag', (), {})()
- try:
- yield bag
- except TypeError as exc:
- if "unexpected keyword argument 'env'" not in str(exc):
- raise
- else:
- return
- warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
- with mock.patch.dict('os.environ', env):
- bag.value = super().spawn(cmd)
-
- # -- Miscellaneous methods -----------------------------------------
- # These are all used by the 'gen_lib_options() function, in
- # ccompiler.py.
-
- def library_dir_option(self, dir):
- return "/LIBPATH:" + dir
-
- def runtime_library_dir_option(self, dir):
- raise DistutilsPlatformError(
- "don't know how to set runtime library search path for MSVC"
- )
-
- def library_option(self, lib):
- return self.library_filename(lib)
-
- def find_library_file(self, dirs, lib, debug=False):
- # Prefer a debugging library if found (and requested), but deal
- # with it if we don't have one.
- if debug:
- try_names = [lib + "_d", lib]
- else:
- try_names = [lib]
- for dir in dirs:
- for name in try_names:
- libfile = os.path.join(dir, self.library_filename(name))
- if os.path.isfile(libfile):
- return libfile
- else:
- # Oops, didn't find it in *any* of 'dirs'
- return None
+ return msvc._get_vc_env
+ raise AttributeError(name)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
index 5bb6df763de..d860f552723 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
@@ -3,7 +3,10 @@
Utility functions for creating archive files (tarballs, zip files,
that sort of thing)."""
+from __future__ import annotations
+
import os
+from typing import Literal, overload
try:
import zipfile
@@ -54,14 +57,14 @@ def _get_uid(name):
def make_tarball(
- base_name,
- base_dir,
- compress="gzip",
- verbose=False,
- dry_run=False,
- owner=None,
- group=None,
-):
+ base_name: str,
+ base_dir: str | os.PathLike[str],
+ compress: Literal["gzip", "bzip2", "xz"] | None = "gzip",
+ verbose: bool = False,
+ dry_run: bool = False,
+ owner: str | None = None,
+ group: str | None = None,
+) -> str:
"""Create a (possibly compressed) tar file from all the files under
'base_dir'.
@@ -122,7 +125,12 @@ def make_tarball(
return archive_name
-def make_zipfile(base_name, base_dir, verbose=False, dry_run=False): # noqa: C901
+def make_zipfile( # noqa: C901
+ base_name: str,
+ base_dir: str | os.PathLike[str],
+ verbose: bool = False,
+ dry_run: bool = False,
+) -> str:
"""Create a zip file from all the files under 'base_dir'.
The output zip file will be named 'base_name' + ".zip". Uses either the
@@ -204,16 +212,38 @@ def check_archive_formats(formats):
return None
+@overload
+def make_archive(
+ base_name: str,
+ format: str,
+ root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+ base_dir: str | None = None,
+ verbose: bool = False,
+ dry_run: bool = False,
+ owner: str | None = None,
+ group: str | None = None,
+) -> str: ...
+@overload
+def make_archive(
+ base_name: str | os.PathLike[str],
+ format: str,
+ root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ base_dir: str | None = None,
+ verbose: bool = False,
+ dry_run: bool = False,
+ owner: str | None = None,
+ group: str | None = None,
+) -> str: ...
def make_archive(
- base_name,
- format,
- root_dir=None,
- base_dir=None,
- verbose=False,
- dry_run=False,
- owner=None,
- group=None,
-):
+ base_name: str | os.PathLike[str],
+ format: str,
+ root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+ base_dir: str | None = None,
+ verbose: bool = False,
+ dry_run: bool = False,
+ owner: str | None = None,
+ group: str | None = None,
+) -> str:
"""Create an archive file (eg. zip or tar).
'base_name' is the name of the file to create, minus any format-specific
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py
index 714f13d8d30..58bc6a55e24 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py
@@ -1,1263 +1,26 @@
-"""distutils.ccompiler
-
-Contains CCompiler, an abstract base class that defines the interface
-for the Distutils compiler abstraction model."""
-
-import os
-import pathlib
-import re
-import sys
-import types
-import warnings
-
-from more_itertools import always_iterable
-
-from ._log import log
-from ._modified import newer_group
-from .dir_util import mkpath
-from .errors import (
- CompileError,
- DistutilsModuleError,
- DistutilsPlatformError,
- LinkError,
- UnknownFileError,
+from .compat.numpy import ( # noqa: F401
+ _default_compilers,
+ compiler_class,
)
-from .file_util import move_file
-from .spawn import spawn
-from .util import execute, is_mingw, split_quoted
-
-
-class CCompiler:
- """Abstract base class to define the interface that must be implemented
- by real compiler classes. Also has some utility methods used by
- several compiler classes.
-
- The basic idea behind a compiler abstraction class is that each
- instance can be used for all the compile/link steps in building a
- single project. Thus, attributes common to all of those compile and
- link steps -- include directories, macros to define, libraries to link
- against, etc. -- are attributes of the compiler instance. To allow for
- variability in how individual files are treated, most of those
- attributes may be varied on a per-compilation or per-link basis.
- """
-
- # 'compiler_type' is a class attribute that identifies this class. It
- # keeps code that wants to know what kind of compiler it's dealing with
- # from having to import all possible compiler classes just to do an
- # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type'
- # should really, really be one of the keys of the 'compiler_class'
- # dictionary (see below -- used by the 'new_compiler()' factory
- # function) -- authors of new compiler interface classes are
- # responsible for updating 'compiler_class'!
- compiler_type = None
-
- # XXX things not handled by this compiler abstraction model:
- # * client can't provide additional options for a compiler,
- # e.g. warning, optimization, debugging flags. Perhaps this
- # should be the domain of concrete compiler abstraction classes
- # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
- # class should have methods for the common ones.
- # * can't completely override the include or library searchg
- # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
- # I'm not sure how widely supported this is even by Unix
- # compilers, much less on other platforms. And I'm even less
- # sure how useful it is; maybe for cross-compiling, but
- # support for that is a ways off. (And anyways, cross
- # compilers probably have a dedicated binary with the
- # right paths compiled in. I hope.)
- # * can't do really freaky things with the library list/library
- # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
- # different versions of libfoo.a in different locations. I
- # think this is useless without the ability to null out the
- # library search path anyways.
-
- # Subclasses that rely on the standard filename generation methods
- # implemented below should override these; see the comment near
- # those methods ('object_filenames()' et. al.) for details:
- src_extensions = None # list of strings
- obj_extension = None # string
- static_lib_extension = None
- shared_lib_extension = None # string
- static_lib_format = None # format string
- shared_lib_format = None # prob. same as static_lib_format
- exe_extension = None # string
-
- # Default language settings. language_map is used to detect a source
- # file or Extension target language, checking source filenames.
- # language_order is used to detect the language precedence, when deciding
- # what language to use when mixing source types. For example, if some
- # extension has two files with ".c" extension, and one with ".cpp", it
- # is still linked as c++.
- language_map = {
- ".c": "c",
- ".cc": "c++",
- ".cpp": "c++",
- ".cxx": "c++",
- ".m": "objc",
- }
- language_order = ["c++", "objc", "c"]
-
- include_dirs = []
- """
- include dirs specific to this compiler class
- """
-
- library_dirs = []
- """
- library dirs specific to this compiler class
- """
-
- def __init__(self, verbose=False, dry_run=False, force=False):
- self.dry_run = dry_run
- self.force = force
- self.verbose = verbose
-
- # 'output_dir': a common output directory for object, library,
- # shared object, and shared library files
- self.output_dir = None
-
- # 'macros': a list of macro definitions (or undefinitions). A
- # macro definition is a 2-tuple (name, value), where the value is
- # either a string or None (no explicit value). A macro
- # undefinition is a 1-tuple (name,).
- self.macros = []
-
- # 'include_dirs': a list of directories to search for include files
- self.include_dirs = []
-
- # 'libraries': a list of libraries to include in any link
- # (library names, not filenames: eg. "foo" not "libfoo.a")
- self.libraries = []
-
- # 'library_dirs': a list of directories to search for libraries
- self.library_dirs = []
-
- # 'runtime_library_dirs': a list of directories to search for
- # shared libraries/objects at runtime
- self.runtime_library_dirs = []
-
- # 'objects': a list of object files (or similar, such as explicitly
- # named library files) to include on any link
- self.objects = []
-
- for key in self.executables.keys():
- self.set_executable(key, self.executables[key])
-
- def set_executables(self, **kwargs):
- """Define the executables (and options for them) that will be run
- to perform the various stages of compilation. The exact set of
- executables that may be specified here depends on the compiler
- class (via the 'executables' class attribute), but most will have:
- compiler the C/C++ compiler
- linker_so linker used to create shared objects and libraries
- linker_exe linker used to create binary executables
- archiver static library creator
-
- On platforms with a command-line (Unix, DOS/Windows), each of these
- is a string that will be split into executable name and (optional)
- list of arguments. (Splitting the string is done similarly to how
- Unix shells operate: words are delimited by spaces, but quotes and
- backslashes can override this. See
- 'distutils.util.split_quoted()'.)
- """
-
- # Note that some CCompiler implementation classes will define class
- # attributes 'cpp', 'cc', etc. with hard-coded executable names;
- # this is appropriate when a compiler class is for exactly one
- # compiler/OS combination (eg. MSVCCompiler). Other compiler
- # classes (UnixCCompiler, in particular) are driven by information
- # discovered at run-time, since there are many different ways to do
- # basically the same things with Unix C compilers.
-
- for key in kwargs:
- if key not in self.executables:
- raise ValueError(
- f"unknown executable '{key}' for class {self.__class__.__name__}"
- )
- self.set_executable(key, kwargs[key])
-
- def set_executable(self, key, value):
- if isinstance(value, str):
- setattr(self, key, split_quoted(value))
- else:
- setattr(self, key, value)
-
- def _find_macro(self, name):
- i = 0
- for defn in self.macros:
- if defn[0] == name:
- return i
- i += 1
- return None
-
- def _check_macro_definitions(self, definitions):
- """Ensure that every element of 'definitions' is valid."""
- for defn in definitions:
- self._check_macro_definition(*defn)
-
- def _check_macro_definition(self, defn):
- """
- Raise a TypeError if defn is not valid.
-
- A valid definition is either a (name, value) 2-tuple or a (name,) tuple.
- """
- if not isinstance(defn, tuple) or not self._is_valid_macro(*defn):
- raise TypeError(
- f"invalid macro definition '{defn}': "
- "must be tuple (string,), (string, string), or (string, None)"
- )
-
- @staticmethod
- def _is_valid_macro(name, value=None):
- """
- A valid macro is a ``name : str`` and a ``value : str | None``.
- """
- return isinstance(name, str) and isinstance(value, (str, types.NoneType))
-
- # -- Bookkeeping methods -------------------------------------------
-
- def define_macro(self, name, value=None):
- """Define a preprocessor macro for all compilations driven by this
- compiler object. The optional parameter 'value' should be a
- string; if it is not supplied, then the macro will be defined
- without an explicit value and the exact outcome depends on the
- compiler used (XXX true? does ANSI say anything about this?)
- """
- # Delete from the list of macro definitions/undefinitions if
- # already there (so that this one will take precedence).
- i = self._find_macro(name)
- if i is not None:
- del self.macros[i]
-
- self.macros.append((name, value))
-
- def undefine_macro(self, name):
- """Undefine a preprocessor macro for all compilations driven by
- this compiler object. If the same macro is defined by
- 'define_macro()' and undefined by 'undefine_macro()' the last call
- takes precedence (including multiple redefinitions or
- undefinitions). If the macro is redefined/undefined on a
- per-compilation basis (ie. in the call to 'compile()'), then that
- takes precedence.
- """
- # Delete from the list of macro definitions/undefinitions if
- # already there (so that this one will take precedence).
- i = self._find_macro(name)
- if i is not None:
- del self.macros[i]
-
- undefn = (name,)
- self.macros.append(undefn)
-
- def add_include_dir(self, dir):
- """Add 'dir' to the list of directories that will be searched for
- header files. The compiler is instructed to search directories in
- the order in which they are supplied by successive calls to
- 'add_include_dir()'.
- """
- self.include_dirs.append(dir)
-
- def set_include_dirs(self, dirs):
- """Set the list of directories that will be searched to 'dirs' (a
- list of strings). Overrides any preceding calls to
- 'add_include_dir()'; subsequence calls to 'add_include_dir()' add
- to the list passed to 'set_include_dirs()'. This does not affect
- any list of standard include directories that the compiler may
- search by default.
- """
- self.include_dirs = dirs[:]
-
- def add_library(self, libname):
- """Add 'libname' to the list of libraries that will be included in
- all links driven by this compiler object. Note that 'libname'
- should *not* be the name of a file containing a library, but the
- name of the library itself: the actual filename will be inferred by
- the linker, the compiler, or the compiler class (depending on the
- platform).
-
- The linker will be instructed to link against libraries in the
- order they were supplied to 'add_library()' and/or
- 'set_libraries()'. It is perfectly valid to duplicate library
- names; the linker will be instructed to link against libraries as
- many times as they are mentioned.
- """
- self.libraries.append(libname)
-
- def set_libraries(self, libnames):
- """Set the list of libraries to be included in all links driven by
- this compiler object to 'libnames' (a list of strings). This does
- not affect any standard system libraries that the linker may
- include by default.
- """
- self.libraries = libnames[:]
-
- def add_library_dir(self, dir):
- """Add 'dir' to the list of directories that will be searched for
- libraries specified to 'add_library()' and 'set_libraries()'. The
- linker will be instructed to search for libraries in the order they
- are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.
- """
- self.library_dirs.append(dir)
-
- def set_library_dirs(self, dirs):
- """Set the list of library search directories to 'dirs' (a list of
- strings). This does not affect any standard library search path
- that the linker may search by default.
- """
- self.library_dirs = dirs[:]
-
- def add_runtime_library_dir(self, dir):
- """Add 'dir' to the list of directories that will be searched for
- shared libraries at runtime.
- """
- self.runtime_library_dirs.append(dir)
-
- def set_runtime_library_dirs(self, dirs):
- """Set the list of directories to search for shared libraries at
- runtime to 'dirs' (a list of strings). This does not affect any
- standard search path that the runtime linker may search by
- default.
- """
- self.runtime_library_dirs = dirs[:]
-
- def add_link_object(self, object):
- """Add 'object' to the list of object files (or analogues, such as
- explicitly named library files or the output of "resource
- compilers") to be included in every link driven by this compiler
- object.
- """
- self.objects.append(object)
-
- def set_link_objects(self, objects):
- """Set the list of object files (or analogues) to be included in
- every link to 'objects'. This does not affect any standard object
- files that the linker may include by default (such as system
- libraries).
- """
- self.objects = objects[:]
-
- # -- Private utility methods --------------------------------------
- # (here for the convenience of subclasses)
-
- # Helper method to prep compiler in subclass compile() methods
-
- def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra):
- """Process arguments and decide which source files to compile."""
- outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
-
- if extra is None:
- extra = []
-
- # Get the list of expected output (object) files
- objects = self.object_filenames(sources, strip_dir=False, output_dir=outdir)
- assert len(objects) == len(sources)
-
- pp_opts = gen_preprocess_options(macros, incdirs)
-
- build = {}
- for i in range(len(sources)):
- src = sources[i]
- obj = objects[i]
- ext = os.path.splitext(src)[1]
- self.mkpath(os.path.dirname(obj))
- build[obj] = (src, ext)
-
- return macros, objects, extra, pp_opts, build
-
- def _get_cc_args(self, pp_opts, debug, before):
- # works for unixccompiler, cygwinccompiler
- cc_args = pp_opts + ['-c']
- if debug:
- cc_args[:0] = ['-g']
- if before:
- cc_args[:0] = before
- return cc_args
-
- def _fix_compile_args(self, output_dir, macros, include_dirs):
- """Typecheck and fix-up some of the arguments to the 'compile()'
- method, and return fixed-up values. Specifically: if 'output_dir'
- is None, replaces it with 'self.output_dir'; ensures that 'macros'
- is a list, and augments it with 'self.macros'; ensures that
- 'include_dirs' is a list, and augments it with 'self.include_dirs'.
- Guarantees that the returned values are of the correct type,
- i.e. for 'output_dir' either string or None, and for 'macros' and
- 'include_dirs' either list or None.
- """
- if output_dir is None:
- output_dir = self.output_dir
- elif not isinstance(output_dir, str):
- raise TypeError("'output_dir' must be a string or None")
-
- if macros is None:
- macros = list(self.macros)
- elif isinstance(macros, list):
- macros = macros + (self.macros or [])
- else:
- raise TypeError("'macros' (if supplied) must be a list of tuples")
-
- if include_dirs is None:
- include_dirs = list(self.include_dirs)
- elif isinstance(include_dirs, (list, tuple)):
- include_dirs = list(include_dirs) + (self.include_dirs or [])
- else:
- raise TypeError("'include_dirs' (if supplied) must be a list of strings")
-
- # add include dirs for class
- include_dirs += self.__class__.include_dirs
-
- return output_dir, macros, include_dirs
-
- def _prep_compile(self, sources, output_dir, depends=None):
- """Decide which source files must be recompiled.
-
- Determine the list of object files corresponding to 'sources',
- and figure out which ones really need to be recompiled.
- Return a list of all object files and a dictionary telling
- which source files can be skipped.
- """
- # Get the list of expected output (object) files
- objects = self.object_filenames(sources, output_dir=output_dir)
- assert len(objects) == len(sources)
-
- # Return an empty dict for the "which source files can be skipped"
- # return value to preserve API compatibility.
- return objects, {}
-
- def _fix_object_args(self, objects, output_dir):
- """Typecheck and fix up some arguments supplied to various methods.
- Specifically: ensure that 'objects' is a list; if output_dir is
- None, replace with self.output_dir. Return fixed versions of
- 'objects' and 'output_dir'.
- """
- if not isinstance(objects, (list, tuple)):
- raise TypeError("'objects' must be a list or tuple of strings")
- objects = list(objects)
-
- if output_dir is None:
- output_dir = self.output_dir
- elif not isinstance(output_dir, str):
- raise TypeError("'output_dir' must be a string or None")
-
- return (objects, output_dir)
-
- def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
- """Typecheck and fix up some of the arguments supplied to the
- 'link_*' methods. Specifically: ensure that all arguments are
- lists, and augment them with their permanent versions
- (eg. 'self.libraries' augments 'libraries'). Return a tuple with
- fixed versions of all arguments.
- """
- if libraries is None:
- libraries = list(self.libraries)
- elif isinstance(libraries, (list, tuple)):
- libraries = list(libraries) + (self.libraries or [])
- else:
- raise TypeError("'libraries' (if supplied) must be a list of strings")
-
- if library_dirs is None:
- library_dirs = list(self.library_dirs)
- elif isinstance(library_dirs, (list, tuple)):
- library_dirs = list(library_dirs) + (self.library_dirs or [])
- else:
- raise TypeError("'library_dirs' (if supplied) must be a list of strings")
-
- # add library dirs for class
- library_dirs += self.__class__.library_dirs
-
- if runtime_library_dirs is None:
- runtime_library_dirs = list(self.runtime_library_dirs)
- elif isinstance(runtime_library_dirs, (list, tuple)):
- runtime_library_dirs = list(runtime_library_dirs) + (
- self.runtime_library_dirs or []
- )
- else:
- raise TypeError(
- "'runtime_library_dirs' (if supplied) must be a list of strings"
- )
-
- return (libraries, library_dirs, runtime_library_dirs)
-
- def _need_link(self, objects, output_file):
- """Return true if we need to relink the files listed in 'objects'
- to recreate 'output_file'.
- """
- if self.force:
- return True
- else:
- if self.dry_run:
- newer = newer_group(objects, output_file, missing='newer')
- else:
- newer = newer_group(objects, output_file)
- return newer
-
- def detect_language(self, sources):
- """Detect the language of a given file, or list of files. Uses
- language_map, and language_order to do the job.
- """
- if not isinstance(sources, list):
- sources = [sources]
- lang = None
- index = len(self.language_order)
- for source in sources:
- base, ext = os.path.splitext(source)
- extlang = self.language_map.get(ext)
- try:
- extindex = self.language_order.index(extlang)
- if extindex < index:
- lang = extlang
- index = extindex
- except ValueError:
- pass
- return lang
-
- # -- Worker methods ------------------------------------------------
- # (must be implemented by subclasses)
-
- def preprocess(
- self,
- source,
- output_file=None,
- macros=None,
- include_dirs=None,
- extra_preargs=None,
- extra_postargs=None,
- ):
- """Preprocess a single C/C++ source file, named in 'source'.
- Output will be written to file named 'output_file', or stdout if
- 'output_file' not supplied. 'macros' is a list of macro
- definitions as for 'compile()', which will augment the macros set
- with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a
- list of directory names that will be added to the default list.
-
- Raises PreprocessError on failure.
- """
- pass
-
- def compile(
- self,
- sources,
- output_dir=None,
- macros=None,
- include_dirs=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- depends=None,
- ):
- """Compile one or more source files.
-
- 'sources' must be a list of filenames, most likely C/C++
- files, but in reality anything that can be handled by a
- particular compiler and compiler class (eg. MSVCCompiler can
- handle resource files in 'sources'). Return a list of object
- filenames, one per source filename in 'sources'. Depending on
- the implementation, not all source files will necessarily be
- compiled, but all corresponding object filenames will be
- returned.
-
- If 'output_dir' is given, object files will be put under it, while
- retaining their original path component. That is, "foo/bar.c"
- normally compiles to "foo/bar.o" (for a Unix implementation); if
- 'output_dir' is "build", then it would compile to
- "build/foo/bar.o".
-
- 'macros', if given, must be a list of macro definitions. A macro
- definition is either a (name, value) 2-tuple or a (name,) 1-tuple.
- The former defines a macro; if the value is None, the macro is
- defined without an explicit value. The 1-tuple case undefines a
- macro. Later definitions/redefinitions/ undefinitions take
- precedence.
-
- 'include_dirs', if given, must be a list of strings, the
- directories to add to the default include file search path for this
- compilation only.
-
- 'debug' is a boolean; if true, the compiler will be instructed to
- output debug symbols in (or alongside) the object file(s).
-
- 'extra_preargs' and 'extra_postargs' are implementation- dependent.
- On platforms that have the notion of a command-line (e.g. Unix,
- DOS/Windows), they are most likely lists of strings: extra
- command-line arguments to prepend/append to the compiler command
- line. On other platforms, consult the implementation class
- documentation. In any event, they are intended as an escape hatch
- for those occasions when the abstract compiler framework doesn't
- cut the mustard.
-
- 'depends', if given, is a list of filenames that all targets
- depend on. If a source file is older than any file in
- depends, then the source file will be recompiled. This
- supports dependency tracking, but only at a coarse
- granularity.
-
- Raises CompileError on failure.
- """
- # A concrete compiler class can either override this method
- # entirely or implement _compile().
- macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
- output_dir, macros, include_dirs, sources, depends, extra_postargs
- )
- cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
-
- for obj in objects:
- try:
- src, ext = build[obj]
- except KeyError:
- continue
- self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
-
- # Return *all* object filenames, not just the ones we just built.
- return objects
-
- def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
- """Compile 'src' to product 'obj'."""
- # A concrete compiler class that does not override compile()
- # should implement _compile().
- pass
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=False, target_lang=None
- ):
- """Link a bunch of stuff together to create a static library file.
- The "bunch of stuff" consists of the list of object files supplied
- as 'objects', the extra object files supplied to
- 'add_link_object()' and/or 'set_link_objects()', the libraries
- supplied to 'add_library()' and/or 'set_libraries()', and the
- libraries supplied as 'libraries' (if any).
-
- 'output_libname' should be a library name, not a filename; the
- filename will be inferred from the library name. 'output_dir' is
- the directory where the library file will be put.
-
- 'debug' is a boolean; if true, debugging information will be
- included in the library (note that on most platforms, it is the
- compile step where this matters: the 'debug' flag is included here
- just for consistency).
-
- 'target_lang' is the target language for which the given objects
- are being compiled. This allows specific linkage time treatment of
- certain languages.
-
- Raises LibError on failure.
- """
- pass
-
- # values for target_desc parameter in link()
- SHARED_OBJECT = "shared_object"
- SHARED_LIBRARY = "shared_library"
- EXECUTABLE = "executable"
-
- def link(
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- """Link a bunch of stuff together to create an executable or
- shared library file.
-
- The "bunch of stuff" consists of the list of object files supplied
- as 'objects'. 'output_filename' should be a filename. If
- 'output_dir' is supplied, 'output_filename' is relative to it
- (i.e. 'output_filename' can provide directory components if
- needed).
-
- 'libraries' is a list of libraries to link against. These are
- library names, not filenames, since they're translated into
- filenames in a platform-specific way (eg. "foo" becomes "libfoo.a"
- on Unix and "foo.lib" on DOS/Windows). However, they can include a
- directory component, which means the linker will look in that
- specific directory rather than searching all the normal locations.
-
- 'library_dirs', if supplied, should be a list of directories to
- search for libraries that were specified as bare library names
- (ie. no directory component). These are on top of the system
- default and those supplied to 'add_library_dir()' and/or
- 'set_library_dirs()'. 'runtime_library_dirs' is a list of
- directories that will be embedded into the shared library and used
- to search for other shared libraries that *it* depends on at
- run-time. (This may only be relevant on Unix.)
-
- 'export_symbols' is a list of symbols that the shared library will
- export. (This appears to be relevant only on Windows.)
-
- 'debug' is as for 'compile()' and 'create_static_lib()', with the
- slight distinction that it actually matters on most platforms (as
- opposed to 'create_static_lib()', which includes a 'debug' flag
- mostly for form's sake).
-
- 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except
- of course that they supply command-line arguments for the
- particular linker being used).
-
- 'target_lang' is the target language for which the given objects
- are being compiled. This allows specific linkage time treatment of
- certain languages.
-
- Raises LinkError on failure.
- """
- raise NotImplementedError
-
- # Old 'link_*()' methods, rewritten to use the new 'link()' method.
-
- def link_shared_lib(
- self,
- objects,
- output_libname,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- self.link(
- CCompiler.SHARED_LIBRARY,
- objects,
- self.library_filename(output_libname, lib_type='shared'),
- output_dir,
- libraries,
- library_dirs,
- runtime_library_dirs,
- export_symbols,
- debug,
- extra_preargs,
- extra_postargs,
- build_temp,
- target_lang,
- )
-
- def link_shared_object(
- self,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- self.link(
- CCompiler.SHARED_OBJECT,
- objects,
- output_filename,
- output_dir,
- libraries,
- library_dirs,
- runtime_library_dirs,
- export_symbols,
- debug,
- extra_preargs,
- extra_postargs,
- build_temp,
- target_lang,
- )
-
- def link_executable(
- self,
- objects,
- output_progname,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- target_lang=None,
- ):
- self.link(
- CCompiler.EXECUTABLE,
- objects,
- self.executable_filename(output_progname),
- output_dir,
- libraries,
- library_dirs,
- runtime_library_dirs,
- None,
- debug,
- extra_preargs,
- extra_postargs,
- None,
- target_lang,
- )
-
- # -- Miscellaneous methods -----------------------------------------
- # These are all used by the 'gen_lib_options() function; there is
- # no appropriate default implementation so subclasses should
- # implement all of these.
-
- def library_dir_option(self, dir):
- """Return the compiler option to add 'dir' to the list of
- directories searched for libraries.
- """
- raise NotImplementedError
-
- def runtime_library_dir_option(self, dir):
- """Return the compiler option to add 'dir' to the list of
- directories searched for runtime libraries.
- """
- raise NotImplementedError
-
- def library_option(self, lib):
- """Return the compiler option to add 'lib' to the list of libraries
- linked into the shared library or executable.
- """
- raise NotImplementedError
-
- def has_function( # noqa: C901
- self,
- funcname,
- includes=None,
- include_dirs=None,
- libraries=None,
- library_dirs=None,
- ):
- """Return a boolean indicating whether funcname is provided as
- a symbol on the current platform. The optional arguments can
- be used to augment the compilation environment.
-
- The libraries argument is a list of flags to be passed to the
- linker to make additional symbol definitions available for
- linking.
-
- The includes and include_dirs arguments are deprecated.
- Usually, supplying include files with function declarations
- will cause function detection to fail even in cases where the
- symbol is available for linking.
-
- """
- # this can't be included at module scope because it tries to
- # import math which might not be available at that point - maybe
- # the necessary logic should just be inlined?
- import tempfile
-
- if includes is None:
- includes = []
- else:
- warnings.warn("includes is deprecated", DeprecationWarning)
- if include_dirs is None:
- include_dirs = []
- else:
- warnings.warn("include_dirs is deprecated", DeprecationWarning)
- if libraries is None:
- libraries = []
- if library_dirs is None:
- library_dirs = []
- fd, fname = tempfile.mkstemp(".c", funcname, text=True)
- with os.fdopen(fd, "w", encoding='utf-8') as f:
- for incl in includes:
- f.write(f"""#include "{incl}"\n""")
- if not includes:
- # Use "char func(void);" as the prototype to follow
- # what autoconf does. This prototype does not match
- # any well-known function the compiler might recognize
- # as a builtin, so this ends up as a true link test.
- # Without a fake prototype, the test would need to
- # know the exact argument types, and the has_function
- # interface does not provide that level of information.
- f.write(
- f"""\
-#ifdef __cplusplus
-extern "C"
-#endif
-char {funcname}(void);
-"""
- )
- f.write(
- f"""\
-int main (int argc, char **argv) {{
- {funcname}();
- return 0;
-}}
-"""
- )
-
- try:
- objects = self.compile([fname], include_dirs=include_dirs)
- except CompileError:
- return False
- finally:
- os.remove(fname)
-
- try:
- self.link_executable(
- objects, "a.out", libraries=libraries, library_dirs=library_dirs
- )
- except (LinkError, TypeError):
- return False
- else:
- os.remove(
- self.executable_filename("a.out", output_dir=self.output_dir or '')
- )
- finally:
- for fn in objects:
- os.remove(fn)
- return True
-
- def find_library_file(self, dirs, lib, debug=False):
- """Search the specified list of directories for a static or shared
- library file 'lib' and return the full path to that file. If
- 'debug' true, look for a debugging version (if that makes sense on
- the current platform). Return None if 'lib' wasn't found in any of
- the specified directories.
- """
- raise NotImplementedError
-
- # -- Filename generation methods -----------------------------------
-
- # The default implementation of the filename generating methods are
- # prejudiced towards the Unix/DOS/Windows view of the world:
- # * object files are named by replacing the source file extension
- # (eg. .c/.cpp -> .o/.obj)
- # * library files (shared or static) are named by plugging the
- # library name and extension into a format string, eg.
- # "lib%s.%s" % (lib_name, ".a") for Unix static libraries
- # * executables are named by appending an extension (possibly
- # empty) to the program name: eg. progname + ".exe" for
- # Windows
- #
- # To reduce redundant code, these methods expect to find
- # several attributes in the current object (presumably defined
- # as class attributes):
- # * src_extensions -
- # list of C/C++ source file extensions, eg. ['.c', '.cpp']
- # * obj_extension -
- # object file extension, eg. '.o' or '.obj'
- # * static_lib_extension -
- # extension for static library files, eg. '.a' or '.lib'
- # * shared_lib_extension -
- # extension for shared library/object files, eg. '.so', '.dll'
- # * static_lib_format -
- # format string for generating static library filenames,
- # eg. 'lib%s.%s' or '%s.%s'
- # * shared_lib_format
- # format string for generating shared library filenames
- # (probably same as static_lib_format, since the extension
- # is one of the intended parameters to the format string)
- # * exe_extension -
- # extension for executable files, eg. '' or '.exe'
-
- def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
- if output_dir is None:
- output_dir = ''
- return list(
- self._make_out_path(output_dir, strip_dir, src_name)
- for src_name in source_filenames
- )
-
- @property
- def out_extensions(self):
- return dict.fromkeys(self.src_extensions, self.obj_extension)
-
- def _make_out_path(self, output_dir, strip_dir, src_name):
- return self._make_out_path_exts(
- output_dir, strip_dir, src_name, self.out_extensions
- )
-
- @classmethod
- def _make_out_path_exts(cls, output_dir, strip_dir, src_name, extensions):
- r"""
- >>> exts = {'.c': '.o'}
- >>> CCompiler._make_out_path_exts('.', False, '/foo/bar.c', exts).replace('\\', '/')
- './foo/bar.o'
- >>> CCompiler._make_out_path_exts('.', True, '/foo/bar.c', exts).replace('\\', '/')
- './bar.o'
- """
- src = pathlib.PurePath(src_name)
- # Ensure base is relative to honor output_dir (python/cpython#37775).
- base = cls._make_relative(src)
- try:
- new_ext = extensions[src.suffix]
- except LookupError:
- raise UnknownFileError(f"unknown file type '{src.suffix}' (from '{src}')")
- if strip_dir:
- base = pathlib.PurePath(base.name)
- return os.path.join(output_dir, base.with_suffix(new_ext))
-
- @staticmethod
- def _make_relative(base: pathlib.Path):
- return base.relative_to(base.anchor)
-
- def shared_object_filename(self, basename, strip_dir=False, output_dir=''):
- assert output_dir is not None
- if strip_dir:
- basename = os.path.basename(basename)
- return os.path.join(output_dir, basename + self.shared_lib_extension)
-
- def executable_filename(self, basename, strip_dir=False, output_dir=''):
- assert output_dir is not None
- if strip_dir:
- basename = os.path.basename(basename)
- return os.path.join(output_dir, basename + (self.exe_extension or ''))
-
- def library_filename(
- self,
- libname,
- lib_type='static',
- strip_dir=False,
- output_dir='', # or 'shared'
- ):
- assert output_dir is not None
- expected = '"static", "shared", "dylib", "xcode_stub"'
- if lib_type not in eval(expected):
- raise ValueError(f"'lib_type' must be {expected}")
- fmt = getattr(self, lib_type + "_lib_format")
- ext = getattr(self, lib_type + "_lib_extension")
-
- dir, base = os.path.split(libname)
- filename = fmt % (base, ext)
- if strip_dir:
- dir = ''
-
- return os.path.join(output_dir, dir, filename)
-
- # -- Utility methods -----------------------------------------------
-
- def announce(self, msg, level=1):
- log.debug(msg)
-
- def debug_print(self, msg):
- from distutils.debug import DEBUG
-
- if DEBUG:
- print(msg)
-
- def warn(self, msg):
- sys.stderr.write(f"warning: {msg}\n")
-
- def execute(self, func, args, msg=None, level=1):
- execute(func, args, msg, self.dry_run)
-
- def spawn(self, cmd, **kwargs):
- spawn(cmd, dry_run=self.dry_run, **kwargs)
-
- def move_file(self, src, dst):
- return move_file(src, dst, dry_run=self.dry_run)
-
- def mkpath(self, name, mode=0o777):
- mkpath(name, mode, dry_run=self.dry_run)
-
-
-# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
-# type for that platform. Keys are interpreted as re match
-# patterns. Order is important; platform mappings are preferred over
-# OS names.
-_default_compilers = (
- # Platform string mappings
- # on a cygwin built python we can use gcc like an ordinary UNIXish
- # compiler
- ('cygwin.*', 'unix'),
- ('zos', 'zos'),
- # OS name mappings
- ('posix', 'unix'),
- ('nt', 'msvc'),
+from .compilers.C import base
+from .compilers.C.base import (
+ gen_lib_options,
+ gen_preprocess_options,
+ get_default_compiler,
+ new_compiler,
+ show_compilers,
)
+from .compilers.C.errors import CompileError, LinkError
+__all__ = [
+ 'CompileError',
+ 'LinkError',
+ 'gen_lib_options',
+ 'gen_preprocess_options',
+ 'get_default_compiler',
+ 'new_compiler',
+ 'show_compilers',
+]
-def get_default_compiler(osname=None, platform=None):
- """Determine the default compiler to use for the given platform.
-
- osname should be one of the standard Python OS names (i.e. the
- ones returned by os.name) and platform the common value
- returned by sys.platform for the platform in question.
-
- The default values are os.name and sys.platform in case the
- parameters are not given.
- """
- if osname is None:
- osname = os.name
- if platform is None:
- platform = sys.platform
- # Mingw is a special case where sys.platform is 'win32' but we
- # want to use the 'mingw32' compiler, so check it first
- if is_mingw():
- return 'mingw32'
- for pattern, compiler in _default_compilers:
- if (
- re.match(pattern, platform) is not None
- or re.match(pattern, osname) is not None
- ):
- return compiler
- # Default to Unix compiler
- return 'unix'
-
-
-# Map compiler types to (module_name, class_name) pairs -- ie. where to
-# find the code that implements an interface to this compiler. (The module
-# is assumed to be in the 'distutils' package.)
-compiler_class = {
- 'unix': ('unixccompiler', 'UnixCCompiler', "standard UNIX-style compiler"),
- 'msvc': ('_msvccompiler', 'MSVCCompiler', "Microsoft Visual C++"),
- 'cygwin': (
- 'cygwinccompiler',
- 'CygwinCCompiler',
- "Cygwin port of GNU C Compiler for Win32",
- ),
- 'mingw32': (
- 'cygwinccompiler',
- 'Mingw32CCompiler',
- "Mingw32 port of GNU C Compiler for Win32",
- ),
- 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"),
- 'zos': ('zosccompiler', 'zOSCCompiler', 'IBM XL C/C++ Compilers'),
-}
-
-
-def show_compilers():
- """Print list of available compilers (used by the "--help-compiler"
- options to "build", "build_ext", "build_clib").
- """
- # XXX this "knows" that the compiler option it's describing is
- # "--compiler", which just happens to be the case for the three
- # commands that use it.
- from distutils.fancy_getopt import FancyGetopt
-
- compilers = sorted(
- ("compiler=" + compiler, None, compiler_class[compiler][2])
- for compiler in compiler_class.keys()
- )
- pretty_printer = FancyGetopt(compilers)
- pretty_printer.print_help("List of available compilers:")
-
-
-def new_compiler(plat=None, compiler=None, verbose=False, dry_run=False, force=False):
- """Generate an instance of some CCompiler subclass for the supplied
- platform/compiler combination. 'plat' defaults to 'os.name'
- (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
- for that platform. Currently only 'posix' and 'nt' are supported, and
- the default compilers are "traditional Unix interface" (UnixCCompiler
- class) and Visual C++ (MSVCCompiler class). Note that it's perfectly
- possible to ask for a Unix compiler object under Windows, and a
- Microsoft compiler object under Unix -- if you supply a value for
- 'compiler', 'plat' is ignored.
- """
- if plat is None:
- plat = os.name
-
- try:
- if compiler is None:
- compiler = get_default_compiler(plat)
-
- (module_name, class_name, long_description) = compiler_class[compiler]
- except KeyError:
- msg = f"don't know how to compile C/C++ code on platform '{plat}'"
- if compiler is not None:
- msg = msg + f" with '{compiler}' compiler"
- raise DistutilsPlatformError(msg)
-
- try:
- module_name = "distutils." + module_name
- __import__(module_name)
- module = sys.modules[module_name]
- klass = vars(module)[class_name]
- except ImportError:
- raise DistutilsModuleError(
- f"can't compile C/C++ code: unable to load module '{module_name}'"
- )
- except KeyError:
- raise DistutilsModuleError(
- f"can't compile C/C++ code: unable to find class '{class_name}' "
- f"in module '{module_name}'"
- )
-
- # XXX The None is necessary to preserve backwards compatibility
- # with classes that expect verbose to be the first positional
- # argument.
- return klass(None, dry_run, force)
-
-
-def gen_preprocess_options(macros, include_dirs):
- """Generate C pre-processor options (-D, -U, -I) as used by at least
- two types of compilers: the typical Unix compiler and Visual C++.
- 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
- means undefine (-U) macro 'name', and (name,value) means define (-D)
- macro 'name' to 'value'. 'include_dirs' is just a list of directory
- names to be added to the header file search path (-I). Returns a list
- of command-line options suitable for either Unix compilers or Visual
- C++.
- """
- # XXX it would be nice (mainly aesthetic, and so we don't generate
- # stupid-looking command lines) to go over 'macros' and eliminate
- # redundant definitions/undefinitions (ie. ensure that only the
- # latest mention of a particular macro winds up on the command
- # line). I don't think it's essential, though, since most (all?)
- # Unix C compilers only pay attention to the latest -D or -U
- # mention of a macro on their command line. Similar situation for
- # 'include_dirs'. I'm punting on both for now. Anyways, weeding out
- # redundancies like this should probably be the province of
- # CCompiler, since the data structures used are inherited from it
- # and therefore common to all CCompiler classes.
- pp_opts = []
- for macro in macros:
- if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2):
- raise TypeError(
- f"bad macro definition '{macro}': "
- "each element of 'macros' list must be a 1- or 2-tuple"
- )
-
- if len(macro) == 1: # undefine this macro
- pp_opts.append(f"-U{macro[0]}")
- elif len(macro) == 2:
- if macro[1] is None: # define with no explicit value
- pp_opts.append(f"-D{macro[0]}")
- else:
- # XXX *don't* need to be clever about quoting the
- # macro value here, because we're going to avoid the
- # shell at all costs when we spawn the command!
- pp_opts.append("-D{}={}".format(*macro))
-
- pp_opts.extend(f"-I{dir}" for dir in include_dirs)
- return pp_opts
-
-
-def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
- """Generate linker options for searching library directories and
- linking with specific libraries. 'libraries' and 'library_dirs' are,
- respectively, lists of library names (not filenames!) and search
- directories. Returns a list of command-line options suitable for use
- with some compiler (depending on the two format strings passed in).
- """
- lib_opts = [compiler.library_dir_option(dir) for dir in library_dirs]
-
- for dir in runtime_library_dirs:
- lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir)))
-
- # XXX it's important that we *not* remove redundant library mentions!
- # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
- # resolve all symbols. I just hope we never have to say "-lfoo obj.o
- # -lbar" to get things to work -- that's certainly a possibility, but a
- # pretty nasty way to arrange your C code.
- for lib in libraries:
- (lib_dir, lib_name) = os.path.split(lib)
- if lib_dir:
- lib_file = compiler.find_library_file([lib_dir], lib_name)
- if lib_file:
- lib_opts.append(lib_file)
- else:
- compiler.warn(
- f"no library file corresponding to '{lib}' found (skipping)"
- )
- else:
- lib_opts.append(compiler.library_option(lib))
- return lib_opts
+CCompiler = base.Compiler
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py b/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py
index 9c6fa6566ce..241621bd513 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/cmd.py
@@ -10,13 +10,24 @@ import logging
import os
import re
import sys
-from collections.abc import Callable
-from typing import Any, ClassVar, TypeVar, overload
+from abc import abstractmethod
+from collections.abc import Callable, MutableSequence
+from typing import TYPE_CHECKING, Any, ClassVar, TypeVar, overload
from . import _modified, archive_util, dir_util, file_util, util
from ._log import log
from .errors import DistutilsOptionError
+if TYPE_CHECKING:
+ # type-only import because of mutual dependence between these classes
+ from distutils.dist import Distribution
+
+ from typing_extensions import TypeVarTuple, Unpack
+
+ _Ts = TypeVarTuple("_Ts")
+
+_StrPathT = TypeVar("_StrPathT", bound="str | os.PathLike[str]")
+_BytesPathT = TypeVar("_BytesPathT", bound="bytes | os.PathLike[bytes]")
_CommandT = TypeVar("_CommandT", bound="Command")
@@ -61,7 +72,7 @@ class Command:
# -- Creation/initialization methods -------------------------------
- def __init__(self, dist):
+ def __init__(self, dist: Distribution) -> None:
"""Create and initialize a new Command object. Most importantly,
invokes the 'initialize_options()' method, which is the real
initializer and depends on the actual command being
@@ -119,7 +130,7 @@ class Command:
else:
raise AttributeError(attr)
- def ensure_finalized(self):
+ def ensure_finalized(self) -> None:
if not self.finalized:
self.finalize_options()
self.finalized = True
@@ -137,7 +148,8 @@ class Command:
# run the command: do whatever it is we're here to do,
# controlled by the command's various option values
- def initialize_options(self):
+ @abstractmethod
+ def initialize_options(self) -> None:
"""Set default values for all the options that this command
supports. Note that these defaults may be overridden by other
commands, by the setup script, by config files, or by the
@@ -151,7 +163,8 @@ class Command:
f"abstract method -- subclass {self.__class__} must override"
)
- def finalize_options(self):
+ @abstractmethod
+ def finalize_options(self) -> None:
"""Set final values for all the options that this command supports.
This is always called as late as possible, ie. after any option
assignments from the command-line or from other commands have been
@@ -180,7 +193,8 @@ class Command:
value = getattr(self, option)
self.announce(indent + f"{option} = {value}", level=logging.INFO)
- def run(self):
+ @abstractmethod
+ def run(self) -> None:
"""A command's raison d'etre: carry out the action it exists to
perform, controlled by the options initialized in
'initialize_options()', customized by other commands, the setup
@@ -194,10 +208,10 @@ class Command:
f"abstract method -- subclass {self.__class__} must override"
)
- def announce(self, msg, level=logging.DEBUG):
+ def announce(self, msg: object, level: int = logging.DEBUG) -> None:
log.log(level, msg)
- def debug_print(self, msg):
+ def debug_print(self, msg: object) -> None:
"""Print 'msg' to stdout if the global DEBUG (taken from the
DISTUTILS_DEBUG environment variable) flag is true.
"""
@@ -229,13 +243,13 @@ class Command:
raise DistutilsOptionError(f"'{option}' must be a {what} (got `{val}`)")
return val
- def ensure_string(self, option, default=None):
+ def ensure_string(self, option: str, default: str | None = None) -> None:
"""Ensure that 'option' is a string; if not defined, set it to
'default'.
"""
self._ensure_stringlike(option, "string", default)
- def ensure_string_list(self, option):
+ def ensure_string_list(self, option: str) -> None:
r"""Ensure that 'option' is a list of strings. If 'option' is
currently a string, we split it either on /,\s*/ or /\s+/, so
"foo bar baz", "foo,bar,baz", and "foo, bar baz" all become
@@ -263,13 +277,13 @@ class Command:
("error in '%s' option: " + error_fmt) % (option, val)
)
- def ensure_filename(self, option):
+ def ensure_filename(self, option: str) -> None:
"""Ensure that 'option' is the name of an existing file."""
self._ensure_tested_string(
option, os.path.isfile, "filename", "'%s' does not exist or is not a file"
)
- def ensure_dirname(self, option):
+ def ensure_dirname(self, option: str) -> None:
self._ensure_tested_string(
option,
os.path.isdir,
@@ -279,13 +293,15 @@ class Command:
# -- Convenience methods for commands ------------------------------
- def get_command_name(self):
+ def get_command_name(self) -> str:
if hasattr(self, 'command_name'):
return self.command_name
else:
return self.__class__.__name__
- def set_undefined_options(self, src_cmd, *option_pairs):
+ def set_undefined_options(
+ self, src_cmd: str, *option_pairs: tuple[str, str]
+ ) -> None:
"""Set the values of any "undefined" options from corresponding
option values in some other command object. "Undefined" here means
"is None", which is the convention used to indicate that an option
@@ -306,7 +322,9 @@ class Command:
if getattr(self, dst_option) is None:
setattr(self, dst_option, getattr(src_cmd_obj, src_option))
- def get_finalized_command(self, command, create=True):
+ # NOTE: Because distutils is private to Setuptools and not all commands are exposed here,
+ # not every possible command is enumerated in the signature.
+ def get_finalized_command(self, command: str, create: bool = True) -> Command:
"""Wrapper around Distribution's 'get_command_obj()' method: find
(create if necessary and 'create' is true) the command object for
'command', call its 'ensure_finalized()' method, and return the
@@ -331,14 +349,14 @@ class Command:
) -> Command:
return self.distribution.reinitialize_command(command, reinit_subcommands)
- def run_command(self, command):
+ def run_command(self, command: str) -> None:
"""Run some other command: uses the 'run_command()' method of
Distribution, which creates and finalizes the command object if
necessary and then invokes its 'run()' method.
"""
self.distribution.run_command(command)
- def get_sub_commands(self):
+ def get_sub_commands(self) -> list[str]:
"""Determine the sub-commands that are relevant in the current
distribution (ie., that need to be run). This is based on the
'sub_commands' class attribute: each tuple in that list may include
@@ -353,24 +371,50 @@ class Command:
# -- External world manipulation -----------------------------------
- def warn(self, msg):
+ def warn(self, msg: object) -> None:
log.warning("warning: %s: %s\n", self.get_command_name(), msg)
- def execute(self, func, args, msg=None, level=1):
+ def execute(
+ self,
+ func: Callable[[Unpack[_Ts]], object],
+ args: tuple[Unpack[_Ts]],
+ msg: object = None,
+ level: int = 1,
+ ) -> None:
util.execute(func, args, msg, dry_run=self.dry_run)
- def mkpath(self, name, mode=0o777):
+ def mkpath(self, name: str, mode: int = 0o777) -> None:
dir_util.mkpath(name, mode, dry_run=self.dry_run)
+ @overload
def copy_file(
self,
- infile,
- outfile,
- preserve_mode=True,
- preserve_times=True,
- link=None,
- level=1,
- ):
+ infile: str | os.PathLike[str],
+ outfile: _StrPathT,
+ preserve_mode: bool = True,
+ preserve_times: bool = True,
+ link: str | None = None,
+ level: int = 1,
+ ) -> tuple[_StrPathT | str, bool]: ...
+ @overload
+ def copy_file(
+ self,
+ infile: bytes | os.PathLike[bytes],
+ outfile: _BytesPathT,
+ preserve_mode: bool = True,
+ preserve_times: bool = True,
+ link: str | None = None,
+ level: int = 1,
+ ) -> tuple[_BytesPathT | bytes, bool]: ...
+ def copy_file(
+ self,
+ infile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ outfile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ preserve_mode: bool = True,
+ preserve_times: bool = True,
+ link: str | None = None,
+ level: int = 1,
+ ) -> tuple[str | os.PathLike[str] | bytes | os.PathLike[bytes], bool]:
"""Copy a file respecting verbose, dry-run and force flags. (The
former two default to whatever is in the Distribution object, and
the latter defaults to false for commands that don't define it.)"""
@@ -386,13 +430,13 @@ class Command:
def copy_tree(
self,
- infile,
- outfile,
- preserve_mode=True,
- preserve_times=True,
- preserve_symlinks=False,
- level=1,
- ):
+ infile: str | os.PathLike[str],
+ outfile: str,
+ preserve_mode: bool = True,
+ preserve_times: bool = True,
+ preserve_symlinks: bool = False,
+ level: int = 1,
+ ) -> list[str]:
"""Copy an entire directory tree respecting verbose, dry-run,
and force flags.
"""
@@ -406,19 +450,60 @@ class Command:
dry_run=self.dry_run,
)
- def move_file(self, src, dst, level=1):
+ @overload
+ def move_file(
+ self, src: str | os.PathLike[str], dst: _StrPathT, level: int = 1
+ ) -> _StrPathT | str: ...
+ @overload
+ def move_file(
+ self, src: bytes | os.PathLike[bytes], dst: _BytesPathT, level: int = 1
+ ) -> _BytesPathT | bytes: ...
+ def move_file(
+ self,
+ src: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ dst: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ level: int = 1,
+ ) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]:
"""Move a file respecting dry-run flag."""
return file_util.move_file(src, dst, dry_run=self.dry_run)
- def spawn(self, cmd, search_path=True, level=1):
+ def spawn(
+ self, cmd: MutableSequence[str], search_path: bool = True, level: int = 1
+ ) -> None:
"""Spawn an external command respecting dry-run flag."""
from distutils.spawn import spawn
spawn(cmd, search_path, dry_run=self.dry_run)
+ @overload
+ def make_archive(
+ self,
+ base_name: str,
+ format: str,
+ root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+ base_dir: str | None = None,
+ owner: str | None = None,
+ group: str | None = None,
+ ) -> str: ...
+ @overload
+ def make_archive(
+ self,
+ base_name: str | os.PathLike[str],
+ format: str,
+ root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ base_dir: str | None = None,
+ owner: str | None = None,
+ group: str | None = None,
+ ) -> str: ...
def make_archive(
- self, base_name, format, root_dir=None, base_dir=None, owner=None, group=None
- ):
+ self,
+ base_name: str | os.PathLike[str],
+ format: str,
+ root_dir: str | os.PathLike[str] | bytes | os.PathLike[bytes] | None = None,
+ base_dir: str | None = None,
+ owner: str | None = None,
+ group: str | None = None,
+ ) -> str:
return archive_util.make_archive(
base_name,
format,
@@ -430,8 +515,15 @@ class Command:
)
def make_file(
- self, infiles, outfile, func, args, exec_msg=None, skip_msg=None, level=1
- ):
+ self,
+ infiles: str | list[str] | tuple[str, ...],
+ outfile: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ func: Callable[[Unpack[_Ts]], object],
+ args: tuple[Unpack[_Ts]],
+ exec_msg: object = None,
+ skip_msg: object = None,
+ level: int = 1,
+ ) -> None:
"""Special case of 'execute()' for operations that process one or
more input files and generate one output file. Works just like
'execute()', except the operation is skipped and a different
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py
index 1ec3c35f40e..07811aab27a 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist.py
@@ -3,14 +3,24 @@
Implements the Distutils 'bdist' command (create a built [binary]
distribution)."""
+from __future__ import annotations
+
import os
import warnings
-from typing import ClassVar
+from collections.abc import Callable
+from typing import TYPE_CHECKING, ClassVar
from ..core import Command
from ..errors import DistutilsOptionError, DistutilsPlatformError
from ..util import get_platform
+if TYPE_CHECKING:
+ from typing_extensions import deprecated
+else:
+
+ def deprecated(message):
+ return lambda fn: fn
+
def show_formats():
"""Print list of available formats (arguments to "--format" option)."""
@@ -26,9 +36,10 @@ def show_formats():
class ListCompat(dict[str, tuple[str, str]]):
# adapter to allow for Setuptools compatibility in format_commands
- def append(self, item):
+ @deprecated("format_commands is now a dict. append is deprecated.")
+ def append(self, item: object) -> None:
warnings.warn(
- """format_commands is now a dict. append is deprecated.""",
+ "format_commands is now a dict. append is deprecated.",
DeprecationWarning,
stacklevel=2,
)
@@ -64,9 +75,9 @@ class bdist(Command):
),
]
- boolean_options = ['skip-build']
+ boolean_options: ClassVar[list[str]] = ['skip-build']
- help_options = [
+ help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
('help-formats', None, "lists available distribution formats", show_formats),
]
@@ -75,7 +86,7 @@ class bdist(Command):
# This won't do in reality: will need to distinguish RPM-ish Linux,
# Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
- default_format = {'posix': 'gztar', 'nt': 'zip'}
+ default_format: ClassVar[dict[str, str]] = {'posix': 'gztar', 'nt': 'zip'}
# Define commands in preferred order for the --help-formats option
format_commands = ListCompat({
@@ -100,7 +111,7 @@ class bdist(Command):
self.group = None
self.owner = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
# have to finalize 'plat_name' before 'bdist_base'
if self.plat_name is None:
if self.skip_build:
@@ -128,7 +139,7 @@ class bdist(Command):
if self.dist_dir is None:
self.dist_dir = "dist"
- def run(self):
+ def run(self) -> None:
# Figure out which sub-commands we need to run.
commands = []
for format in self.formats:
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py
index 67b0c8cce9f..ccad66f4312 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_dumb.py
@@ -6,6 +6,7 @@ $exec_prefix)."""
import os
from distutils._log import log
+from typing import ClassVar
from ..core import Command
from ..dir_util import ensure_relative, remove_tree
@@ -54,7 +55,7 @@ class bdist_dumb(Command):
),
]
- boolean_options = ['keep-temp', 'skip-build', 'relative']
+ boolean_options: ClassVar[list[str]] = ['keep-temp', 'skip-build', 'relative']
default_format = {'posix': 'gztar', 'nt': 'zip'}
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py
index d443eb09b53..357b4e861e2 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/bdist_rpm.py
@@ -7,6 +7,7 @@ import os
import subprocess
import sys
from distutils._log import log
+from typing import ClassVar
from ..core import Command
from ..debug import DEBUG
@@ -136,7 +137,7 @@ class bdist_rpm(Command):
('quiet', 'q', "Run the INSTALL phase of RPM building in quiet mode"),
]
- boolean_options = [
+ boolean_options: ClassVar[list[str]] = [
'keep-temp',
'use-rpm-opt-flags',
'rpm3-mode',
@@ -144,7 +145,7 @@ class bdist_rpm(Command):
'quiet',
]
- negative_opt = {
+ negative_opt: ClassVar[dict[str, str]] = {
'no-keep-temp': 'keep-temp',
'no-rpm-opt-flags': 'use-rpm-opt-flags',
'rpm2-mode': 'rpm3-mode',
@@ -195,7 +196,7 @@ class bdist_rpm(Command):
self.force_arch = None
self.quiet = False
- def finalize_options(self):
+ def finalize_options(self) -> None:
self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
if self.rpm_base is None:
if not self.rpm3_mode:
@@ -228,7 +229,7 @@ class bdist_rpm(Command):
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
self.finalize_package_data()
- def finalize_package_data(self):
+ def finalize_package_data(self) -> None:
self.ensure_string('group', "Development/Libraries")
self.ensure_string(
'vendor',
@@ -274,7 +275,7 @@ class bdist_rpm(Command):
self.ensure_string('force_arch')
- def run(self): # noqa: C901
+ def run(self) -> None: # noqa: C901
if DEBUG:
print("before _get_package_data():")
print("vendor =", self.vendor)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/build.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build.py
index ccd2c706a3e..6a8303a954e 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build.py
@@ -2,21 +2,20 @@
Implements the Distutils 'build' command."""
+from __future__ import annotations
+
import os
import sys
import sysconfig
+from collections.abc import Callable
+from typing import ClassVar
+from ..ccompiler import show_compilers
from ..core import Command
from ..errors import DistutilsOptionError
from ..util import get_platform
-def show_compilers():
- from ..ccompiler import show_compilers
-
- show_compilers()
-
-
class build(Command):
description = "build everything needed to install"
@@ -43,9 +42,9 @@ class build(Command):
('executable=', 'e', "specify final destination interpreter path (build.py)"),
]
- boolean_options = ['debug', 'force']
+ boolean_options: ClassVar[list[str]] = ['debug', 'force']
- help_options = [
+ help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
('help-compiler', None, "list available compilers", show_compilers),
]
@@ -65,7 +64,7 @@ class build(Command):
self.executable = None
self.parallel = None
- def finalize_options(self): # noqa: C901
+ def finalize_options(self) -> None: # noqa: C901
if self.plat_name is None:
self.plat_name = get_platform()
else:
@@ -126,7 +125,7 @@ class build(Command):
except ValueError:
raise DistutilsOptionError("parallel should be an integer")
- def run(self):
+ def run(self) -> None:
# Run all relevant sub-commands. This will be some subset of:
# - build_py - pure Python modules
# - build_clib - standalone C libraries
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py
index 3e1832768b2..8b65b3d8ece 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_clib.py
@@ -4,7 +4,6 @@ Implements the Distutils 'build_clib' command, to build a C/C++ library
that is included in the module distribution and needed by an extension
module."""
-
# XXX this module has *lots* of code ripped-off quite transparently from
# build_ext.py -- not surprisingly really, as the work required to build
# a static library from a collection of C source files is not really all
@@ -13,22 +12,19 @@ module."""
# necessary refactoring to account for the overlap in code between the
# two modules, mainly because a number of subtle details changed in the
# cut 'n paste. Sigh.
+from __future__ import annotations
import os
+from collections.abc import Callable
from distutils._log import log
from typing import ClassVar
+from ..ccompiler import new_compiler, show_compilers
from ..core import Command
from ..errors import DistutilsSetupError
from ..sysconfig import customize_compiler
-def show_compilers():
- from ..ccompiler import show_compilers
-
- show_compilers()
-
-
class build_clib(Command):
description = "build C/C++ libraries used by Python extensions"
@@ -40,9 +36,9 @@ class build_clib(Command):
('compiler=', 'c', "specify the compiler type"),
]
- boolean_options = ['debug', 'force']
+ boolean_options: ClassVar[list[str]] = ['debug', 'force']
- help_options = [
+ help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
('help-compiler', None, "list available compilers", show_compilers),
]
@@ -61,7 +57,7 @@ class build_clib(Command):
self.force = False
self.compiler = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
# This might be confusing: both build-clib and build-temp default
# to build-temp as defined by the "build" command. This is because
# I think that C libraries are really just temporary build
@@ -88,13 +84,10 @@ class build_clib(Command):
# XXX same as for build_ext -- what about 'self.define' and
# 'self.undef' ?
- def run(self):
+ def run(self) -> None:
if not self.libraries:
return
- # Yech -- this is cut 'n pasted from build_ext.py!
- from ..ccompiler import new_compiler
-
self.compiler = new_compiler(
compiler=self.compiler, dry_run=self.dry_run, force=self.force
)
@@ -112,7 +105,7 @@ class build_clib(Command):
self.build_libraries(self.libraries)
- def check_library_list(self, libraries):
+ def check_library_list(self, libraries) -> None:
"""Ensure that the list of libraries is valid.
`library` is presumably provided as a command option 'libraries'.
@@ -174,7 +167,7 @@ class build_clib(Command):
filenames.extend(sources)
return filenames
- def build_libraries(self, libraries):
+ def build_libraries(self, libraries) -> None:
for lib_name, build_info in libraries:
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py
index cf60bd0ad82..ec45b4403e5 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_ext.py
@@ -4,14 +4,19 @@ Implements the Distutils 'build_ext' command, for building extension
modules (currently limited to C extensions, should accommodate C++
extensions ASAP)."""
+from __future__ import annotations
+
import contextlib
import os
import re
import sys
+from collections.abc import Callable
from distutils._log import log
from site import USER_BASE
+from typing import ClassVar
from .._modified import newer_group
+from ..ccompiler import new_compiler, show_compilers
from ..core import Command
from ..errors import (
CCompilerError,
@@ -30,12 +35,6 @@ from ..util import get_platform, is_freethreaded, is_mingw
extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9]*)*$')
-def show_compilers():
- from ..ccompiler import show_compilers
-
- show_compilers()
-
-
class build_ext(Command):
description = "build C/C++ extensions (compile/link to build directory)"
@@ -98,9 +97,15 @@ class build_ext(Command):
('user', None, "add user include, library and rpath"),
]
- boolean_options = ['inplace', 'debug', 'force', 'swig-cpp', 'user']
+ boolean_options: ClassVar[list[str]] = [
+ 'inplace',
+ 'debug',
+ 'force',
+ 'swig-cpp',
+ 'user',
+ ]
- help_options = [
+ help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
('help-compiler', None, "list available compilers", show_compilers),
]
@@ -153,7 +158,7 @@ class build_ext(Command):
# building third party extensions
yield sysconfig.get_config_var('LIBDIR')
- def finalize_options(self): # noqa: C901
+ def finalize_options(self) -> None: # noqa: C901
from distutils import sysconfig
self.set_undefined_options(
@@ -292,9 +297,7 @@ class build_ext(Command):
except ValueError:
raise DistutilsOptionError("parallel should be an integer")
- def run(self): # noqa: C901
- from ..ccompiler import new_compiler
-
+ def run(self) -> None: # noqa: C901
# 'self.extensions', as supplied by setup.py, is a list of
# Extension instances. See the documentation for Extension (in
# distutils.extension) for details.
@@ -364,7 +367,7 @@ class build_ext(Command):
# Now actually compile and link everything.
self.build_extensions()
- def check_extensions_list(self, extensions): # noqa: C901
+ def check_extensions_list(self, extensions) -> None: # noqa: C901
"""Ensure that the list of extensions (presumably provided as a
command option 'extensions') is valid, i.e. it is a list of
Extension objects. We also support the old-style list of 2-tuples,
@@ -472,7 +475,7 @@ class build_ext(Command):
# "build" tree.
return [self.get_ext_fullpath(ext.name) for ext in self.extensions]
- def build_extensions(self):
+ def build_extensions(self) -> None:
# First, sanity-check the 'extensions' list
self.check_extensions_list(self.extensions)
if self.parallel:
@@ -515,7 +518,7 @@ class build_ext(Command):
raise
self.warn(f'building extension "{ext.name}" failed: {e}')
- def build_extension(self, ext):
+ def build_extension(self, ext) -> None:
sources = ext.sources
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
@@ -676,7 +679,7 @@ class build_ext(Command):
# -- Name generators -----------------------------------------------
# (extension names, filenames, whatever)
- def get_ext_fullpath(self, ext_name):
+ def get_ext_fullpath(self, ext_name: str) -> str:
"""Returns the path of the filename for a given extension.
The file is located in `build_lib` or directly in the package
@@ -703,7 +706,7 @@ class build_ext(Command):
# package_dir/filename
return os.path.join(package_dir, filename)
- def get_ext_fullname(self, ext_name):
+ def get_ext_fullname(self, ext_name: str) -> str:
"""Returns the fullname of a given extension name.
Adds the `package.` prefix"""
@@ -712,7 +715,7 @@ class build_ext(Command):
else:
return self.package + '.' + ext_name
- def get_ext_filename(self, ext_name):
+ def get_ext_filename(self, ext_name: str) -> str:
r"""Convert the name of an extension (eg. "foo.bar") into the name
of the file from which it will be loaded (eg. "foo/bar.so", or
"foo\bar.pyd").
@@ -723,13 +726,13 @@ class build_ext(Command):
ext_suffix = get_config_var('EXT_SUFFIX')
return os.path.join(*ext_path) + ext_suffix
- def get_export_symbols(self, ext):
+ def get_export_symbols(self, ext: Extension) -> list[str]:
"""Return the list of symbols that a shared extension has to
export. This either uses 'ext.export_symbols' or, if it's not
provided, "PyInit_" + module_name. Only relevant on Windows, where
the .pyd file (DLL) must export the module "PyInit_" function.
"""
- name = ext.name.split('.')[-1]
+ name = self._get_module_name_for_symbol(ext)
try:
# Unicode module name support as defined in PEP-489
# https://peps.python.org/pep-0489/#export-hook-name
@@ -744,7 +747,16 @@ class build_ext(Command):
ext.export_symbols.append(initfunc_name)
return ext.export_symbols
- def get_libraries(self, ext): # noqa: C901
+ def _get_module_name_for_symbol(self, ext):
+ # Package name should be used for `__init__` modules
+ # https://github.com/python/cpython/issues/80074
+ # https://github.com/pypa/setuptools/issues/4826
+ parts = ext.name.split(".")
+ if parts[-1] == "__init__" and len(parts) >= 2:
+ return parts[-2]
+ return parts[-1]
+
+ def get_libraries(self, ext: Extension) -> list[str]: # noqa: C901
"""Return the list of libraries to link against when building a
shared extension. On most platforms, this is just 'ext.libraries';
on Windows, we add the Python library (eg. python20.dll).
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py
index 49d710346eb..a20b076fe7c 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_py.py
@@ -7,6 +7,7 @@ import importlib.util
import os
import sys
from distutils._log import log
+from typing import ClassVar
from ..core import Command
from ..errors import DistutilsFileError, DistutilsOptionError
@@ -29,8 +30,8 @@ class build_py(Command):
('force', 'f', "forcibly build everything (ignore file timestamps)"),
]
- boolean_options = ['compile', 'force']
- negative_opt = {'no-compile': 'compile'}
+ boolean_options: ClassVar[list[str]] = ['compile', 'force']
+ negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}
def initialize_options(self):
self.build_lib = None
@@ -42,7 +43,7 @@ class build_py(Command):
self.optimize = 0
self.force = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
self.set_undefined_options(
'build', ('build_lib', 'build_lib'), ('force', 'force')
)
@@ -67,7 +68,7 @@ class build_py(Command):
except (ValueError, AssertionError):
raise DistutilsOptionError("optimize must be 0, 1, or 2")
- def run(self):
+ def run(self) -> None:
# XXX copy_file by default preserves atime and mtime. IMHO this is
# the right thing to do, but perhaps it should be an option -- in
# particular, a site administrator might want installed files to
@@ -134,7 +135,7 @@ class build_py(Command):
])
return files
- def build_package_data(self):
+ def build_package_data(self) -> None:
"""Copy data files into build directory"""
for _package, src_dir, build_dir, filenames in self.data_files:
for filename in filenames:
@@ -306,7 +307,7 @@ class build_py(Command):
outfile_path = [build_dir] + list(package) + [module + ".py"]
return os.path.join(*outfile_path)
- def get_outputs(self, include_bytecode=True):
+ def get_outputs(self, include_bytecode: bool = True) -> list[str]:
modules = self.find_all_modules()
outputs = []
for package, module, _module_file in modules:
@@ -349,7 +350,7 @@ class build_py(Command):
self.mkpath(dir)
return self.copy_file(module_file, outfile, preserve_mode=False)
- def build_modules(self):
+ def build_modules(self) -> None:
modules = self.find_modules()
for package, module, module_file in modules:
# Now "build" the module -- ie. copy the source file to
@@ -358,7 +359,7 @@ class build_py(Command):
# under self.build_lib.)
self.build_module(module, module_file, package)
- def build_packages(self):
+ def build_packages(self) -> None:
for package in self.packages:
# Get list of (package, module, module_file) tuples based on
# scanning the package directory. 'package' is only included
@@ -378,7 +379,7 @@ class build_py(Command):
assert package == package_
self.build_module(module, module_file, package)
- def byte_compile(self, files):
+ def byte_compile(self, files) -> None:
if sys.dont_write_bytecode:
self.warn('byte-compiling is disabled, skipping.')
return
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py
index 1c6fd3cafff..127c51d8dc2 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/build_scripts.py
@@ -5,7 +5,6 @@ Implements the Distutils 'build_scripts' command."""
import os
import re
import tokenize
-from distutils import sysconfig
from distutils._log import log
from stat import ST_MODE
from typing import ClassVar
@@ -32,7 +31,7 @@ class build_scripts(Command):
('executable=', 'e', "specify final destination interpreter path"),
]
- boolean_options = ['force']
+ boolean_options: ClassVar[list[str]] = ['force']
def initialize_options(self):
self.build_dir = None
@@ -76,7 +75,7 @@ class build_scripts(Command):
return outfiles, updated_files
- def _copy_script(self, script, outfiles, updated_files): # noqa: C901
+ def _copy_script(self, script, outfiles, updated_files):
shebang_match = None
script = convert_path(script)
outfile = os.path.join(self.build_dir, os.path.basename(script))
@@ -106,18 +105,8 @@ class build_scripts(Command):
if shebang_match:
log.info("copying and adjusting %s -> %s", script, self.build_dir)
if not self.dry_run:
- if not sysconfig.python_build:
- executable = self.executable
- else:
- executable = os.path.join(
- sysconfig.get_config_var("BINDIR"),
- "python{}{}".format(
- sysconfig.get_config_var("VERSION"),
- sysconfig.get_config_var("EXE"),
- ),
- )
post_interp = shebang_match.group(1) or ''
- shebang = "#!" + executable + post_interp + "\n"
+ shebang = f"#!python{post_interp}\n"
self._validate_shebang(shebang, f.encoding)
with open(outfile, "w", encoding=f.encoding) as outf:
outf.write(shebang)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py
index 078c1ce87e8..58a823dd390 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/check.py
@@ -52,7 +52,7 @@ class check(Command):
('strict', 's', 'Will exit with an error if a check fails'),
]
- boolean_options = ['metadata', 'restructuredtext', 'strict']
+ boolean_options: ClassVar[list[str]] = ['metadata', 'restructuredtext', 'strict']
def initialize_options(self):
"""Sets default values for options."""
@@ -141,7 +141,7 @@ class check(Command):
document.note_source(source_path, -1)
try:
parser.parse(data, document)
- except AttributeError as e:
+ except (AttributeError, TypeError) as e:
reporter.messages.append((
-1,
f'Could not finish the parsing: {e}.',
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py
index fb54a60ed4f..23427aba211 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/clean.py
@@ -6,6 +6,7 @@ Implements the Distutils 'clean' command."""
import os
from distutils._log import log
+from typing import ClassVar
from ..core import Command
from ..dir_util import remove_tree
@@ -30,7 +31,7 @@ class clean(Command):
('all', 'a', "remove all build output, not just temporary by-products"),
]
- boolean_options = ['all']
+ boolean_options: ClassVar[list[str]] = ['all']
def initialize_options(self):
self.build_base = None
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py
index fe83c2924dc..c825765c873 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/config.py
@@ -17,6 +17,7 @@ import re
from collections.abc import Sequence
from distutils._log import log
+from ..ccompiler import CCompiler, CompileError, LinkError, new_compiler
from ..core import Command
from ..errors import DistutilsExecError
from ..sysconfig import customize_compiler
@@ -88,10 +89,6 @@ class config(Command):
"""Check that 'self.compiler' really is a CCompiler object;
if not, make it one.
"""
- # We do this late, and only on-demand, because this is an expensive
- # import.
- from ..ccompiler import CCompiler, new_compiler
-
if not isinstance(self.compiler, CCompiler):
self.compiler = new_compiler(
compiler=self.compiler, dry_run=self.dry_run, force=True
@@ -177,8 +174,6 @@ class config(Command):
preprocessor succeeded, false if there were any errors.
('body' probably isn't of much use, but what the heck.)
"""
- from ..ccompiler import CompileError
-
self._check_compiler()
ok = True
try:
@@ -213,8 +208,6 @@ class config(Command):
"""Try to compile a source file built from 'body' and 'headers'.
Return true on success, false otherwise.
"""
- from ..ccompiler import CompileError
-
self._check_compiler()
try:
self._compile(body, headers, include_dirs, lang)
@@ -239,8 +232,6 @@ class config(Command):
'headers', to executable form. Return true on success, false
otherwise.
"""
- from ..ccompiler import CompileError, LinkError
-
self._check_compiler()
try:
self._link(body, headers, include_dirs, libraries, library_dirs, lang)
@@ -265,8 +256,6 @@ class config(Command):
built from 'body' and 'headers'. Return true on success, false
otherwise.
"""
- from ..ccompiler import CompileError, LinkError
-
self._check_compiler()
try:
src, obj, exe = self._link(
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py
index 94009950249..dc17e56a807 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py
@@ -2,6 +2,9 @@
Implements the Distutils 'install' command."""
+from __future__ import annotations
+
+import collections
import contextlib
import itertools
import os
@@ -9,8 +12,7 @@ import sys
import sysconfig
from distutils._log import log
from site import USER_BASE, USER_SITE
-
-import jaraco.collections
+from typing import ClassVar
from ..core import Command
from ..debug import DEBUG
@@ -142,7 +144,7 @@ def _resolve_scheme(name):
try:
resolved = sysconfig.get_preferred_scheme(key)
except Exception:
- resolved = fw.scheme(_pypy_hack(name))
+ resolved = fw.scheme(name)
return resolved
@@ -159,7 +161,7 @@ def _inject_headers(name, scheme):
"""
# Bypass the preferred scheme, which may not
# have defined headers.
- fallback = _load_scheme(_pypy_hack(name))
+ fallback = _load_scheme(name)
scheme.setdefault('headers', fallback['headers'])
return scheme
@@ -169,14 +171,6 @@ def _scheme_attrs(scheme):
return {f'install_{key}': scheme[key] for key in SCHEME_KEYS}
-def _pypy_hack(name):
- PY37 = sys.version_info < (3, 8)
- old_pypy = hasattr(sys, 'pypy_version_info') and PY37
- prefix = not name.endswith(('_user', '_home'))
- pypy_name = 'pypy' + '_nt' * (os.name == 'nt')
- return pypy_name if old_pypy and prefix else name
-
-
class install(Command):
description = "install everything from build directory"
@@ -238,7 +232,7 @@ class install(Command):
('record=', None, "filename in which to record list of installed files"),
]
- boolean_options = ['compile', 'force', 'skip-build']
+ boolean_options: ClassVar[list[str]] = ['compile', 'force', 'skip-build']
if HAS_USER_SITE:
user_options.append((
@@ -248,15 +242,15 @@ class install(Command):
))
boolean_options.append('user')
- negative_opt = {'no-compile': 'compile'}
+ negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}
- def initialize_options(self):
+ def initialize_options(self) -> None:
"""Initializes options."""
# High-level options: these select both an installation base
# and scheme.
- self.prefix = None
- self.exec_prefix = None
- self.home = None
+ self.prefix: str | None = None
+ self.exec_prefix: str | None = None
+ self.home: str | None = None
self.user = False
# These select only the installation base; it's up to the user to
@@ -264,7 +258,7 @@ class install(Command):
# the --install-{platlib,purelib,scripts,data} options).
self.install_base = None
self.install_platbase = None
- self.root = None
+ self.root: str | None = None
# These options are the actual installation directories; if not
# supplied by the user, they are filled in using the installation
@@ -273,7 +267,7 @@ class install(Command):
self.install_purelib = None # for pure module distributions
self.install_platlib = None # non-pure (dists w/ extensions)
self.install_headers = None # for C/C++ headers
- self.install_lib = None # set to either purelib or platlib
+ self.install_lib: str | None = None # set to either purelib or platlib
self.install_scripts = None
self.install_data = None
self.install_userbase = USER_BASE
@@ -327,7 +321,7 @@ class install(Command):
# party Python modules on various platforms given a wide
# array of user input is decided. Yes, it's quite complex!)
- def finalize_options(self): # noqa: C901
+ def finalize_options(self) -> None: # noqa: C901
"""Finalizes options."""
# This method (and its helpers, like 'finalize_unix()',
# 'finalize_other()', and 'select_scheme()') is where the default
@@ -429,12 +423,12 @@ class install(Command):
local_vars['userbase'] = self.install_userbase
local_vars['usersite'] = self.install_usersite
- self.config_vars = jaraco.collections.DictStack([
- fw.vars(),
- compat_vars,
- sysconfig.get_config_vars(),
+ self.config_vars = collections.ChainMap(
local_vars,
- ])
+ sysconfig.get_config_vars(),
+ compat_vars,
+ fw.vars(),
+ )
self.expand_basedirs()
@@ -510,7 +504,7 @@ class install(Command):
# Punt on doc directories for now -- after all, we're punting on
# documentation completely!
- def dump_dirs(self, msg):
+ def dump_dirs(self, msg) -> None:
"""Dumps the list of user options."""
if not DEBUG:
return
@@ -530,7 +524,7 @@ class install(Command):
val = getattr(self, opt_name)
log.debug(" %s: %s", opt_name, val)
- def finalize_unix(self):
+ def finalize_unix(self) -> None:
"""Finalizes options for posix platforms."""
if self.install_base is not None or self.install_platbase is not None:
incomplete_scheme = (
@@ -579,7 +573,7 @@ class install(Command):
self.install_platbase = self.exec_prefix
self.select_scheme("posix_prefix")
- def finalize_other(self):
+ def finalize_other(self) -> None:
"""Finalizes options for non-posix platforms"""
if self.user:
if self.install_userbase is None:
@@ -601,7 +595,7 @@ class install(Command):
f"I don't know how to install stuff on '{os.name}'"
)
- def select_scheme(self, name):
+ def select_scheme(self, name) -> None:
_select_scheme(self, name)
def _expand_attrs(self, attrs):
@@ -613,12 +607,12 @@ class install(Command):
val = subst_vars(val, self.config_vars)
setattr(self, attr, val)
- def expand_basedirs(self):
+ def expand_basedirs(self) -> None:
"""Calls `os.path.expanduser` on install_base, install_platbase and
root."""
self._expand_attrs(['install_base', 'install_platbase', 'root'])
- def expand_dirs(self):
+ def expand_dirs(self) -> None:
"""Calls `os.path.expanduser` on install dirs."""
self._expand_attrs([
'install_purelib',
@@ -629,13 +623,13 @@ class install(Command):
'install_data',
])
- def convert_paths(self, *names):
+ def convert_paths(self, *names) -> None:
"""Call `convert_path` over `names`."""
for name in names:
attr = "install_" + name
setattr(self, attr, convert_path(getattr(self, attr)))
- def handle_extra_path(self):
+ def handle_extra_path(self) -> None:
"""Set `path_file` and `extra_dirs` using `extra_path`."""
if self.extra_path is None:
self.extra_path = self.distribution.extra_path
@@ -670,13 +664,13 @@ class install(Command):
self.path_file = path_file
self.extra_dirs = extra_dirs
- def change_roots(self, *names):
+ def change_roots(self, *names) -> None:
"""Change the install directories pointed by name using root."""
for name in names:
attr = "install_" + name
setattr(self, attr, change_root(self.root, getattr(self, attr)))
- def create_home_path(self):
+ def create_home_path(self) -> None:
"""Create directories under ~."""
if not self.user:
return
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py
index 36f5bcc8bf8..4ad186e8ec6 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_data.py
@@ -10,6 +10,7 @@ from __future__ import annotations
import functools
import os
from collections.abc import Iterable
+from typing import ClassVar
from ..core import Command
from ..util import change_root, convert_path
@@ -28,7 +29,7 @@ class install_data(Command):
('force', 'f', "force installation (overwrite existing files)"),
]
- boolean_options = ['force']
+ boolean_options: ClassVar[list[str]] = ['force']
def initialize_options(self):
self.install_dir = None
@@ -38,7 +39,7 @@ class install_data(Command):
self.data_files = self.distribution.data_files
self.warn_dir = True
- def finalize_options(self):
+ def finalize_options(self) -> None:
self.set_undefined_options(
'install',
('install_data', 'install_dir'),
@@ -46,7 +47,7 @@ class install_data(Command):
('force', 'force'),
)
- def run(self):
+ def run(self) -> None:
self.mkpath(self.install_dir)
for f in self.data_files:
self._copy(f)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_headers.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_headers.py
index 586121e0897..97af1371efc 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_headers.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_headers.py
@@ -17,7 +17,7 @@ class install_headers(Command):
('force', 'f', "force installation (overwrite existing files)"),
]
- boolean_options = ['force']
+ boolean_options: ClassVar[list[str]] = ['force']
def initialize_options(self):
self.install_dir = None
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py
index 4c1230a2862..2aababf8004 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_lib.py
@@ -3,9 +3,12 @@
Implements the Distutils 'install_lib' command
(install all Python modules)."""
+from __future__ import annotations
+
import importlib.util
import os
import sys
+from typing import Any, ClassVar
from ..core import Command
from ..errors import DistutilsOptionError
@@ -47,8 +50,8 @@ class install_lib(Command):
('skip-build', None, "skip the build steps"),
]
- boolean_options = ['force', 'compile', 'skip-build']
- negative_opt = {'no-compile': 'compile'}
+ boolean_options: ClassVar[list[str]] = ['force', 'compile', 'skip-build']
+ negative_opt: ClassVar[dict[str, str]] = {'no-compile': 'compile'}
def initialize_options(self):
# let the 'install' command dictate our installation directory
@@ -59,7 +62,7 @@ class install_lib(Command):
self.optimize = None
self.skip_build = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
# Get all the information we need to install pure Python modules
# from the umbrella 'install' command -- build (source) directory,
# install (target) directory, and whether to compile .py files.
@@ -86,7 +89,7 @@ class install_lib(Command):
if self.optimize not in (0, 1, 2):
raise DistutilsOptionError("optimize must be 0, 1, or 2")
- def run(self):
+ def run(self) -> None:
# Make sure we have built everything we need first
self.build()
@@ -102,14 +105,15 @@ class install_lib(Command):
# -- Top-level worker functions ------------------------------------
# (called from 'run()')
- def build(self):
+ def build(self) -> None:
if not self.skip_build:
if self.distribution.has_pure_modules():
self.run_command('build_py')
if self.distribution.has_ext_modules():
self.run_command('build_ext')
- def install(self):
+ # Any: https://typing.readthedocs.io/en/latest/guides/writing_stubs.html#the-any-trick
+ def install(self) -> list[str] | Any:
if os.path.isdir(self.build_dir):
outfiles = self.copy_tree(self.build_dir, self.install_dir)
else:
@@ -119,7 +123,7 @@ class install_lib(Command):
return
return outfiles
- def byte_compile(self, files):
+ def byte_compile(self, files) -> None:
if sys.dont_write_bytecode:
self.warn('byte-compiling is disabled, skipping.')
return
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py
index bb43387fb87..92e8694111e 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install_scripts.py
@@ -8,6 +8,7 @@ Python scripts."""
import os
from distutils._log import log
from stat import ST_MODE
+from typing import ClassVar
from ..core import Command
@@ -22,7 +23,7 @@ class install_scripts(Command):
('skip-build', None, "skip the build steps"),
]
- boolean_options = ['force', 'skip-build']
+ boolean_options: ClassVar[list[str]] = ['force', 'skip-build']
def initialize_options(self):
self.install_dir = None
@@ -30,7 +31,7 @@ class install_scripts(Command):
self.build_dir = None
self.skip_build = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
self.set_undefined_options('build', ('build_scripts', 'build_dir'))
self.set_undefined_options(
'install',
@@ -39,7 +40,7 @@ class install_scripts(Command):
('skip_build', 'skip_build'),
)
- def run(self):
+ def run(self) -> None:
if not self.skip_build:
self.run_command('build_scripts')
self.outfiles = self.copy_tree(self.build_dir, self.install_dir)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
index acb3a41650c..b3bf0c326aa 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
@@ -2,8 +2,11 @@
Implements the Distutils 'sdist' command (create a source distribution)."""
+from __future__ import annotations
+
import os
import sys
+from collections.abc import Callable
from distutils import archive_util, dir_util, file_util
from distutils._log import log
from glob import glob
@@ -34,7 +37,7 @@ def show_formats():
class sdist(Command):
description = "create a source distribution (tarball, zip file, etc.)"
- def checking_metadata(self):
+ def checking_metadata(self) -> bool:
"""Callable used for the check sub-command.
Placed here so user_options can view it"""
@@ -98,7 +101,7 @@ class sdist(Command):
),
]
- boolean_options = [
+ boolean_options: ClassVar[list[str]] = [
'use-defaults',
'prune',
'manifest-only',
@@ -107,11 +110,14 @@ class sdist(Command):
'metadata-check',
]
- help_options = [
+ help_options: ClassVar[list[tuple[str, str | None, str, Callable[[], object]]]] = [
('help-formats', None, "list available distribution formats", show_formats),
]
- negative_opt = {'no-defaults': 'use-defaults', 'no-prune': 'prune'}
+ negative_opt: ClassVar[dict[str, str]] = {
+ 'no-defaults': 'use-defaults',
+ 'no-prune': 'prune',
+ }
sub_commands = [('check', checking_metadata)]
@@ -136,11 +142,11 @@ class sdist(Command):
self.dist_dir = None
self.archive_files = None
- self.metadata_check = 1
+ self.metadata_check = True
self.owner = None
self.group = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
if self.manifest is None:
self.manifest = "MANIFEST"
if self.template is None:
@@ -155,7 +161,7 @@ class sdist(Command):
if self.dist_dir is None:
self.dist_dir = "dist"
- def run(self):
+ def run(self) -> None:
# 'filelist' contains the list of files that will make up the
# manifest
self.filelist = FileList()
@@ -177,7 +183,7 @@ class sdist(Command):
# or zipfile, or whatever.
self.make_distribution()
- def get_file_list(self):
+ def get_file_list(self) -> None:
"""Figure out the list of files to include in the source
distribution, and put it in 'self.filelist'. This might involve
reading the manifest template (and writing the manifest), or just
@@ -218,7 +224,7 @@ class sdist(Command):
self.filelist.remove_duplicates()
self.write_manifest()
- def add_defaults(self):
+ def add_defaults(self) -> None:
"""Add all the default files to self.filelist:
- README or README.txt
- setup.py
@@ -333,7 +339,7 @@ class sdist(Command):
build_scripts = self.get_finalized_command('build_scripts')
self.filelist.extend(build_scripts.get_source_files())
- def read_template(self):
+ def read_template(self) -> None:
"""Read and parse manifest template file named by self.template.
(usually "MANIFEST.in") The parsing and processing is done by
@@ -368,7 +374,7 @@ class sdist(Command):
finally:
template.close()
- def prune_file_list(self):
+ def prune_file_list(self) -> None:
"""Prune off branches that might slip into the file list as created
by 'read_template()', but really don't belong there:
* the build tree (typically "build")
@@ -391,7 +397,7 @@ class sdist(Command):
vcs_ptrn = r'(^|{})({})({}).*'.format(seps, '|'.join(vcs_dirs), seps)
self.filelist.exclude_pattern(vcs_ptrn, is_regex=True)
- def write_manifest(self):
+ def write_manifest(self) -> None:
"""Write the file list in 'self.filelist' (presumably as filled in
by 'add_defaults()' and 'read_template()') to the manifest file
named by 'self.manifest'.
@@ -419,7 +425,7 @@ class sdist(Command):
first_line = next(fp)
return first_line != '# file GENERATED by distutils, do NOT edit\n'
- def read_manifest(self):
+ def read_manifest(self) -> None:
"""Read the manifest file (named by 'self.manifest') and use it to
fill in 'self.filelist', the list of files to include in the source
distribution.
@@ -431,7 +437,7 @@ class sdist(Command):
filter(None, filterfalse(is_comment, map(str.strip, lines)))
)
- def make_release_tree(self, base_dir, files):
+ def make_release_tree(self, base_dir, files) -> None:
"""Create the directory tree that will become the source
distribution archive. All directories implied by the filenames in
'files' are created under 'base_dir', and then we hard link or copy
@@ -473,7 +479,7 @@ class sdist(Command):
self.distribution.metadata.write_pkg_info(base_dir)
- def make_distribution(self):
+ def make_distribution(self) -> None:
"""Create the source distribution(s). First, we create the release
tree with 'make_release_tree()'; then, we create all required
archive files (according to 'self.formats') from the release tree.
@@ -511,5 +517,5 @@ class sdist(Command):
return self.archive_files
-def is_comment(line):
+def is_comment(line: str) -> bool:
return line.startswith('#')
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compat/__init__.py b/contrib/python/setuptools/py3/setuptools/_distutils/compat/__init__.py
index c715ee9cc59..2c43729b09e 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/compat/__init__.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compat/__init__.py
@@ -1,7 +1,12 @@
from __future__ import annotations
+from collections.abc import Iterable
+from typing import TypeVar
-def consolidate_linker_args(args: list[str]) -> list[str] | str:
+_IterableT = TypeVar("_IterableT", bound="Iterable[str]")
+
+
+def consolidate_linker_args(args: _IterableT) -> _IterableT | str:
"""
Ensure the return value is a string for backward compatibility.
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compat/numpy.py b/contrib/python/setuptools/py3/setuptools/_distutils/compat/numpy.py
new file mode 100644
index 00000000000..73eca7acb10
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compat/numpy.py
@@ -0,0 +1,2 @@
+# required for older numpy versions on Pythons prior to 3.12; see pypa/setuptools#4876
+from ..compilers.C.base import _default_compilers, compiler_class # noqa: F401
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/__init__.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/__init__.py
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py
new file mode 100644
index 00000000000..5efd2a39d6a
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/base.py
@@ -0,0 +1,1394 @@
+"""distutils.ccompiler
+
+Contains Compiler, an abstract base class that defines the interface
+for the Distutils compiler abstraction model."""
+
+from __future__ import annotations
+
+import os
+import pathlib
+import re
+import sys
+import warnings
+from collections.abc import Callable, Iterable, MutableSequence, Sequence
+from typing import (
+ TYPE_CHECKING,
+ ClassVar,
+ Literal,
+ TypeVar,
+ Union,
+ overload,
+)
+
+from more_itertools import always_iterable
+
+from ..._log import log
+from ..._modified import newer_group
+from ...dir_util import mkpath
+from ...errors import (
+ DistutilsModuleError,
+ DistutilsPlatformError,
+)
+from ...file_util import move_file
+from ...spawn import spawn
+from ...util import execute, is_mingw, split_quoted
+from .errors import (
+ CompileError,
+ LinkError,
+ UnknownFileType,
+)
+
+if TYPE_CHECKING:
+ from typing_extensions import TypeAlias, TypeVarTuple, Unpack
+
+ _Ts = TypeVarTuple("_Ts")
+
+_Macro: TypeAlias = Union[tuple[str], tuple[str, Union[str, None]]]
+_StrPathT = TypeVar("_StrPathT", bound="str | os.PathLike[str]")
+_BytesPathT = TypeVar("_BytesPathT", bound="bytes | os.PathLike[bytes]")
+
+
+class Compiler:
+ """Abstract base class to define the interface that must be implemented
+ by real compiler classes. Also has some utility methods used by
+ several compiler classes.
+
+ The basic idea behind a compiler abstraction class is that each
+ instance can be used for all the compile/link steps in building a
+ single project. Thus, attributes common to all of those compile and
+ link steps -- include directories, macros to define, libraries to link
+ against, etc. -- are attributes of the compiler instance. To allow for
+ variability in how individual files are treated, most of those
+ attributes may be varied on a per-compilation or per-link basis.
+ """
+
+ # 'compiler_type' is a class attribute that identifies this class. It
+ # keeps code that wants to know what kind of compiler it's dealing with
+ # from having to import all possible compiler classes just to do an
+ # 'isinstance'. In concrete CCompiler subclasses, 'compiler_type'
+ # should really, really be one of the keys of the 'compiler_class'
+ # dictionary (see below -- used by the 'new_compiler()' factory
+ # function) -- authors of new compiler interface classes are
+ # responsible for updating 'compiler_class'!
+ compiler_type: ClassVar[str] = None # type: ignore[assignment]
+
+ # XXX things not handled by this compiler abstraction model:
+ # * client can't provide additional options for a compiler,
+ # e.g. warning, optimization, debugging flags. Perhaps this
+ # should be the domain of concrete compiler abstraction classes
+ # (UnixCCompiler, MSVCCompiler, etc.) -- or perhaps the base
+ # class should have methods for the common ones.
+ # * can't completely override the include or library searchg
+ # path, ie. no "cc -I -Idir1 -Idir2" or "cc -L -Ldir1 -Ldir2".
+ # I'm not sure how widely supported this is even by Unix
+ # compilers, much less on other platforms. And I'm even less
+ # sure how useful it is; maybe for cross-compiling, but
+ # support for that is a ways off. (And anyways, cross
+ # compilers probably have a dedicated binary with the
+ # right paths compiled in. I hope.)
+ # * can't do really freaky things with the library list/library
+ # dirs, e.g. "-Ldir1 -lfoo -Ldir2 -lfoo" to link against
+ # different versions of libfoo.a in different locations. I
+ # think this is useless without the ability to null out the
+ # library search path anyways.
+
+ executables: ClassVar[dict]
+
+ # Subclasses that rely on the standard filename generation methods
+ # implemented below should override these; see the comment near
+ # those methods ('object_filenames()' et. al.) for details:
+ src_extensions: ClassVar[list[str] | None] = None
+ obj_extension: ClassVar[str | None] = None
+ static_lib_extension: ClassVar[str | None] = None
+ shared_lib_extension: ClassVar[str | None] = None
+ static_lib_format: ClassVar[str | None] = None # format string
+ shared_lib_format: ClassVar[str | None] = None # prob. same as static_lib_format
+ exe_extension: ClassVar[str | None] = None
+
+ # Default language settings. language_map is used to detect a source
+ # file or Extension target language, checking source filenames.
+ # language_order is used to detect the language precedence, when deciding
+ # what language to use when mixing source types. For example, if some
+ # extension has two files with ".c" extension, and one with ".cpp", it
+ # is still linked as c++.
+ language_map: ClassVar[dict[str, str]] = {
+ ".c": "c",
+ ".cc": "c++",
+ ".cpp": "c++",
+ ".cxx": "c++",
+ ".m": "objc",
+ }
+ language_order: ClassVar[list[str]] = ["c++", "objc", "c"]
+
+ include_dirs: list[str] = []
+ """
+ include dirs specific to this compiler class
+ """
+
+ library_dirs: list[str] = []
+ """
+ library dirs specific to this compiler class
+ """
+
+ def __init__(
+ self, verbose: bool = False, dry_run: bool = False, force: bool = False
+ ) -> None:
+ self.dry_run = dry_run
+ self.force = force
+ self.verbose = verbose
+
+ # 'output_dir': a common output directory for object, library,
+ # shared object, and shared library files
+ self.output_dir: str | None = None
+
+ # 'macros': a list of macro definitions (or undefinitions). A
+ # macro definition is a 2-tuple (name, value), where the value is
+ # either a string or None (no explicit value). A macro
+ # undefinition is a 1-tuple (name,).
+ self.macros: list[_Macro] = []
+
+ # 'include_dirs': a list of directories to search for include files
+ self.include_dirs = []
+
+ # 'libraries': a list of libraries to include in any link
+ # (library names, not filenames: eg. "foo" not "libfoo.a")
+ self.libraries: list[str] = []
+
+ # 'library_dirs': a list of directories to search for libraries
+ self.library_dirs = []
+
+ # 'runtime_library_dirs': a list of directories to search for
+ # shared libraries/objects at runtime
+ self.runtime_library_dirs: list[str] = []
+
+ # 'objects': a list of object files (or similar, such as explicitly
+ # named library files) to include on any link
+ self.objects: list[str] = []
+
+ for key in self.executables.keys():
+ self.set_executable(key, self.executables[key])
+
+ def set_executables(self, **kwargs: str) -> None:
+ """Define the executables (and options for them) that will be run
+ to perform the various stages of compilation. The exact set of
+ executables that may be specified here depends on the compiler
+ class (via the 'executables' class attribute), but most will have:
+ compiler the C/C++ compiler
+ linker_so linker used to create shared objects and libraries
+ linker_exe linker used to create binary executables
+ archiver static library creator
+
+ On platforms with a command-line (Unix, DOS/Windows), each of these
+ is a string that will be split into executable name and (optional)
+ list of arguments. (Splitting the string is done similarly to how
+ Unix shells operate: words are delimited by spaces, but quotes and
+ backslashes can override this. See
+ 'distutils.util.split_quoted()'.)
+ """
+
+ # Note that some CCompiler implementation classes will define class
+ # attributes 'cpp', 'cc', etc. with hard-coded executable names;
+ # this is appropriate when a compiler class is for exactly one
+ # compiler/OS combination (eg. MSVCCompiler). Other compiler
+ # classes (UnixCCompiler, in particular) are driven by information
+ # discovered at run-time, since there are many different ways to do
+ # basically the same things with Unix C compilers.
+
+ for key in kwargs:
+ if key not in self.executables:
+ raise ValueError(
+ f"unknown executable '{key}' for class {self.__class__.__name__}"
+ )
+ self.set_executable(key, kwargs[key])
+
+ def set_executable(self, key, value):
+ if isinstance(value, str):
+ setattr(self, key, split_quoted(value))
+ else:
+ setattr(self, key, value)
+
+ def _find_macro(self, name):
+ i = 0
+ for defn in self.macros:
+ if defn[0] == name:
+ return i
+ i += 1
+ return None
+
+ def _check_macro_definitions(self, definitions):
+ """Ensure that every element of 'definitions' is valid."""
+ for defn in definitions:
+ self._check_macro_definition(*defn)
+
+ def _check_macro_definition(self, defn):
+ """
+ Raise a TypeError if defn is not valid.
+
+ A valid definition is either a (name, value) 2-tuple or a (name,) tuple.
+ """
+ if not isinstance(defn, tuple) or not self._is_valid_macro(*defn):
+ raise TypeError(
+ f"invalid macro definition '{defn}': "
+ "must be tuple (string,), (string, string), or (string, None)"
+ )
+
+ @staticmethod
+ def _is_valid_macro(name, value=None):
+ """
+ A valid macro is a ``name : str`` and a ``value : str | None``.
+
+ >>> Compiler._is_valid_macro('foo', None)
+ True
+ """
+ return isinstance(name, str) and isinstance(value, (str, type(None)))
+
+ # -- Bookkeeping methods -------------------------------------------
+
+ def define_macro(self, name: str, value: str | None = None) -> None:
+ """Define a preprocessor macro for all compilations driven by this
+ compiler object. The optional parameter 'value' should be a
+ string; if it is not supplied, then the macro will be defined
+ without an explicit value and the exact outcome depends on the
+ compiler used (XXX true? does ANSI say anything about this?)
+ """
+ # Delete from the list of macro definitions/undefinitions if
+ # already there (so that this one will take precedence).
+ i = self._find_macro(name)
+ if i is not None:
+ del self.macros[i]
+
+ self.macros.append((name, value))
+
+ def undefine_macro(self, name: str) -> None:
+ """Undefine a preprocessor macro for all compilations driven by
+ this compiler object. If the same macro is defined by
+ 'define_macro()' and undefined by 'undefine_macro()' the last call
+ takes precedence (including multiple redefinitions or
+ undefinitions). If the macro is redefined/undefined on a
+ per-compilation basis (ie. in the call to 'compile()'), then that
+ takes precedence.
+ """
+ # Delete from the list of macro definitions/undefinitions if
+ # already there (so that this one will take precedence).
+ i = self._find_macro(name)
+ if i is not None:
+ del self.macros[i]
+
+ undefn = (name,)
+ self.macros.append(undefn)
+
+ def add_include_dir(self, dir: str) -> None:
+ """Add 'dir' to the list of directories that will be searched for
+ header files. The compiler is instructed to search directories in
+ the order in which they are supplied by successive calls to
+ 'add_include_dir()'.
+ """
+ self.include_dirs.append(dir)
+
+ def set_include_dirs(self, dirs: list[str]) -> None:
+ """Set the list of directories that will be searched to 'dirs' (a
+ list of strings). Overrides any preceding calls to
+ 'add_include_dir()'; subsequence calls to 'add_include_dir()' add
+ to the list passed to 'set_include_dirs()'. This does not affect
+ any list of standard include directories that the compiler may
+ search by default.
+ """
+ self.include_dirs = dirs[:]
+
+ def add_library(self, libname: str) -> None:
+ """Add 'libname' to the list of libraries that will be included in
+ all links driven by this compiler object. Note that 'libname'
+ should *not* be the name of a file containing a library, but the
+ name of the library itself: the actual filename will be inferred by
+ the linker, the compiler, or the compiler class (depending on the
+ platform).
+
+ The linker will be instructed to link against libraries in the
+ order they were supplied to 'add_library()' and/or
+ 'set_libraries()'. It is perfectly valid to duplicate library
+ names; the linker will be instructed to link against libraries as
+ many times as they are mentioned.
+ """
+ self.libraries.append(libname)
+
+ def set_libraries(self, libnames: list[str]) -> None:
+ """Set the list of libraries to be included in all links driven by
+ this compiler object to 'libnames' (a list of strings). This does
+ not affect any standard system libraries that the linker may
+ include by default.
+ """
+ self.libraries = libnames[:]
+
+ def add_library_dir(self, dir: str) -> None:
+ """Add 'dir' to the list of directories that will be searched for
+ libraries specified to 'add_library()' and 'set_libraries()'. The
+ linker will be instructed to search for libraries in the order they
+ are supplied to 'add_library_dir()' and/or 'set_library_dirs()'.
+ """
+ self.library_dirs.append(dir)
+
+ def set_library_dirs(self, dirs: list[str]) -> None:
+ """Set the list of library search directories to 'dirs' (a list of
+ strings). This does not affect any standard library search path
+ that the linker may search by default.
+ """
+ self.library_dirs = dirs[:]
+
+ def add_runtime_library_dir(self, dir: str) -> None:
+ """Add 'dir' to the list of directories that will be searched for
+ shared libraries at runtime.
+ """
+ self.runtime_library_dirs.append(dir)
+
+ def set_runtime_library_dirs(self, dirs: list[str]) -> None:
+ """Set the list of directories to search for shared libraries at
+ runtime to 'dirs' (a list of strings). This does not affect any
+ standard search path that the runtime linker may search by
+ default.
+ """
+ self.runtime_library_dirs = dirs[:]
+
+ def add_link_object(self, object: str) -> None:
+ """Add 'object' to the list of object files (or analogues, such as
+ explicitly named library files or the output of "resource
+ compilers") to be included in every link driven by this compiler
+ object.
+ """
+ self.objects.append(object)
+
+ def set_link_objects(self, objects: list[str]) -> None:
+ """Set the list of object files (or analogues) to be included in
+ every link to 'objects'. This does not affect any standard object
+ files that the linker may include by default (such as system
+ libraries).
+ """
+ self.objects = objects[:]
+
+ # -- Private utility methods --------------------------------------
+ # (here for the convenience of subclasses)
+
+ # Helper method to prep compiler in subclass compile() methods
+
+ def _setup_compile(
+ self,
+ outdir: str | None,
+ macros: list[_Macro] | None,
+ incdirs: list[str] | tuple[str, ...] | None,
+ sources,
+ depends,
+ extra,
+ ):
+ """Process arguments and decide which source files to compile."""
+ outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
+
+ if extra is None:
+ extra = []
+
+ # Get the list of expected output (object) files
+ objects = self.object_filenames(sources, strip_dir=False, output_dir=outdir)
+ assert len(objects) == len(sources)
+
+ pp_opts = gen_preprocess_options(macros, incdirs)
+
+ build = {}
+ for i in range(len(sources)):
+ src = sources[i]
+ obj = objects[i]
+ ext = os.path.splitext(src)[1]
+ self.mkpath(os.path.dirname(obj))
+ build[obj] = (src, ext)
+
+ return macros, objects, extra, pp_opts, build
+
+ def _get_cc_args(self, pp_opts, debug, before):
+ # works for unixccompiler, cygwinccompiler
+ cc_args = pp_opts + ['-c']
+ if debug:
+ cc_args[:0] = ['-g']
+ if before:
+ cc_args[:0] = before
+ return cc_args
+
+ def _fix_compile_args(
+ self,
+ output_dir: str | None,
+ macros: list[_Macro] | None,
+ include_dirs: list[str] | tuple[str, ...] | None,
+ ) -> tuple[str, list[_Macro], list[str]]:
+ """Typecheck and fix-up some of the arguments to the 'compile()'
+ method, and return fixed-up values. Specifically: if 'output_dir'
+ is None, replaces it with 'self.output_dir'; ensures that 'macros'
+ is a list, and augments it with 'self.macros'; ensures that
+ 'include_dirs' is a list, and augments it with 'self.include_dirs'.
+ Guarantees that the returned values are of the correct type,
+ i.e. for 'output_dir' either string or None, and for 'macros' and
+ 'include_dirs' either list or None.
+ """
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif not isinstance(output_dir, str):
+ raise TypeError("'output_dir' must be a string or None")
+
+ if macros is None:
+ macros = list(self.macros)
+ elif isinstance(macros, list):
+ macros = macros + (self.macros or [])
+ else:
+ raise TypeError("'macros' (if supplied) must be a list of tuples")
+
+ if include_dirs is None:
+ include_dirs = list(self.include_dirs)
+ elif isinstance(include_dirs, (list, tuple)):
+ include_dirs = list(include_dirs) + (self.include_dirs or [])
+ else:
+ raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+
+ # add include dirs for class
+ include_dirs += self.__class__.include_dirs
+
+ return output_dir, macros, include_dirs
+
+ def _prep_compile(self, sources, output_dir, depends=None):
+ """Decide which source files must be recompiled.
+
+ Determine the list of object files corresponding to 'sources',
+ and figure out which ones really need to be recompiled.
+ Return a list of all object files and a dictionary telling
+ which source files can be skipped.
+ """
+ # Get the list of expected output (object) files
+ objects = self.object_filenames(sources, output_dir=output_dir)
+ assert len(objects) == len(sources)
+
+ # Return an empty dict for the "which source files can be skipped"
+ # return value to preserve API compatibility.
+ return objects, {}
+
+ def _fix_object_args(
+ self, objects: list[str] | tuple[str, ...], output_dir: str | None
+ ) -> tuple[list[str], str]:
+ """Typecheck and fix up some arguments supplied to various methods.
+ Specifically: ensure that 'objects' is a list; if output_dir is
+ None, replace with self.output_dir. Return fixed versions of
+ 'objects' and 'output_dir'.
+ """
+ if not isinstance(objects, (list, tuple)):
+ raise TypeError("'objects' must be a list or tuple of strings")
+ objects = list(objects)
+
+ if output_dir is None:
+ output_dir = self.output_dir
+ elif not isinstance(output_dir, str):
+ raise TypeError("'output_dir' must be a string or None")
+
+ return (objects, output_dir)
+
+ def _fix_lib_args(
+ self,
+ libraries: list[str] | tuple[str, ...] | None,
+ library_dirs: list[str] | tuple[str, ...] | None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None,
+ ) -> tuple[list[str], list[str], list[str]]:
+ """Typecheck and fix up some of the arguments supplied to the
+ 'link_*' methods. Specifically: ensure that all arguments are
+ lists, and augment them with their permanent versions
+ (eg. 'self.libraries' augments 'libraries'). Return a tuple with
+ fixed versions of all arguments.
+ """
+ if libraries is None:
+ libraries = list(self.libraries)
+ elif isinstance(libraries, (list, tuple)):
+ libraries = list(libraries) + (self.libraries or [])
+ else:
+ raise TypeError("'libraries' (if supplied) must be a list of strings")
+
+ if library_dirs is None:
+ library_dirs = list(self.library_dirs)
+ elif isinstance(library_dirs, (list, tuple)):
+ library_dirs = list(library_dirs) + (self.library_dirs or [])
+ else:
+ raise TypeError("'library_dirs' (if supplied) must be a list of strings")
+
+ # add library dirs for class
+ library_dirs += self.__class__.library_dirs
+
+ if runtime_library_dirs is None:
+ runtime_library_dirs = list(self.runtime_library_dirs)
+ elif isinstance(runtime_library_dirs, (list, tuple)):
+ runtime_library_dirs = list(runtime_library_dirs) + (
+ self.runtime_library_dirs or []
+ )
+ else:
+ raise TypeError(
+ "'runtime_library_dirs' (if supplied) must be a list of strings"
+ )
+
+ return (libraries, library_dirs, runtime_library_dirs)
+
+ def _need_link(self, objects, output_file):
+ """Return true if we need to relink the files listed in 'objects'
+ to recreate 'output_file'.
+ """
+ if self.force:
+ return True
+ else:
+ if self.dry_run:
+ newer = newer_group(objects, output_file, missing='newer')
+ else:
+ newer = newer_group(objects, output_file)
+ return newer
+
+ def detect_language(self, sources: str | list[str]) -> str | None:
+ """Detect the language of a given file, or list of files. Uses
+ language_map, and language_order to do the job.
+ """
+ if not isinstance(sources, list):
+ sources = [sources]
+ lang = None
+ index = len(self.language_order)
+ for source in sources:
+ base, ext = os.path.splitext(source)
+ extlang = self.language_map.get(ext)
+ try:
+ extindex = self.language_order.index(extlang)
+ if extindex < index:
+ lang = extlang
+ index = extindex
+ except ValueError:
+ pass
+ return lang
+
+ # -- Worker methods ------------------------------------------------
+ # (must be implemented by subclasses)
+
+ def preprocess(
+ self,
+ source: str | os.PathLike[str],
+ output_file: str | os.PathLike[str] | None = None,
+ macros: list[_Macro] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: Iterable[str] | None = None,
+ ):
+ """Preprocess a single C/C++ source file, named in 'source'.
+ Output will be written to file named 'output_file', or stdout if
+ 'output_file' not supplied. 'macros' is a list of macro
+ definitions as for 'compile()', which will augment the macros set
+ with 'define_macro()' and 'undefine_macro()'. 'include_dirs' is a
+ list of directory names that will be added to the default list.
+
+ Raises PreprocessError on failure.
+ """
+ pass
+
+ def compile(
+ self,
+ sources: Sequence[str | os.PathLike[str]],
+ output_dir: str | None = None,
+ macros: list[_Macro] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ depends: list[str] | tuple[str, ...] | None = None,
+ ) -> list[str]:
+ """Compile one or more source files.
+
+ 'sources' must be a list of filenames, most likely C/C++
+ files, but in reality anything that can be handled by a
+ particular compiler and compiler class (eg. MSVCCompiler can
+ handle resource files in 'sources'). Return a list of object
+ filenames, one per source filename in 'sources'. Depending on
+ the implementation, not all source files will necessarily be
+ compiled, but all corresponding object filenames will be
+ returned.
+
+ If 'output_dir' is given, object files will be put under it, while
+ retaining their original path component. That is, "foo/bar.c"
+ normally compiles to "foo/bar.o" (for a Unix implementation); if
+ 'output_dir' is "build", then it would compile to
+ "build/foo/bar.o".
+
+ 'macros', if given, must be a list of macro definitions. A macro
+ definition is either a (name, value) 2-tuple or a (name,) 1-tuple.
+ The former defines a macro; if the value is None, the macro is
+ defined without an explicit value. The 1-tuple case undefines a
+ macro. Later definitions/redefinitions/ undefinitions take
+ precedence.
+
+ 'include_dirs', if given, must be a list of strings, the
+ directories to add to the default include file search path for this
+ compilation only.
+
+ 'debug' is a boolean; if true, the compiler will be instructed to
+ output debug symbols in (or alongside) the object file(s).
+
+ 'extra_preargs' and 'extra_postargs' are implementation- dependent.
+ On platforms that have the notion of a command-line (e.g. Unix,
+ DOS/Windows), they are most likely lists of strings: extra
+ command-line arguments to prepend/append to the compiler command
+ line. On other platforms, consult the implementation class
+ documentation. In any event, they are intended as an escape hatch
+ for those occasions when the abstract compiler framework doesn't
+ cut the mustard.
+
+ 'depends', if given, is a list of filenames that all targets
+ depend on. If a source file is older than any file in
+ depends, then the source file will be recompiled. This
+ supports dependency tracking, but only at a coarse
+ granularity.
+
+ Raises CompileError on failure.
+ """
+ # A concrete compiler class can either override this method
+ # entirely or implement _compile().
+ macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
+ output_dir, macros, include_dirs, sources, depends, extra_postargs
+ )
+ cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
+
+ # Return *all* object filenames, not just the ones we just built.
+ return objects
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ """Compile 'src' to product 'obj'."""
+ # A concrete compiler class that does not override compile()
+ # should implement _compile().
+ pass
+
+ def create_static_lib(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_libname: str,
+ output_dir: str | None = None,
+ debug: bool = False,
+ target_lang: str | None = None,
+ ) -> None:
+ """Link a bunch of stuff together to create a static library file.
+ The "bunch of stuff" consists of the list of object files supplied
+ as 'objects', the extra object files supplied to
+ 'add_link_object()' and/or 'set_link_objects()', the libraries
+ supplied to 'add_library()' and/or 'set_libraries()', and the
+ libraries supplied as 'libraries' (if any).
+
+ 'output_libname' should be a library name, not a filename; the
+ filename will be inferred from the library name. 'output_dir' is
+ the directory where the library file will be put.
+
+ 'debug' is a boolean; if true, debugging information will be
+ included in the library (note that on most platforms, it is the
+ compile step where this matters: the 'debug' flag is included here
+ just for consistency).
+
+ 'target_lang' is the target language for which the given objects
+ are being compiled. This allows specific linkage time treatment of
+ certain languages.
+
+ Raises LibError on failure.
+ """
+ pass
+
+ # values for target_desc parameter in link()
+ SHARED_OBJECT = "shared_object"
+ SHARED_LIBRARY = "shared_library"
+ EXECUTABLE = "executable"
+
+ def link(
+ self,
+ target_desc: str,
+ objects: list[str] | tuple[str, ...],
+ output_filename: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ """Link a bunch of stuff together to create an executable or
+ shared library file.
+
+ The "bunch of stuff" consists of the list of object files supplied
+ as 'objects'. 'output_filename' should be a filename. If
+ 'output_dir' is supplied, 'output_filename' is relative to it
+ (i.e. 'output_filename' can provide directory components if
+ needed).
+
+ 'libraries' is a list of libraries to link against. These are
+ library names, not filenames, since they're translated into
+ filenames in a platform-specific way (eg. "foo" becomes "libfoo.a"
+ on Unix and "foo.lib" on DOS/Windows). However, they can include a
+ directory component, which means the linker will look in that
+ specific directory rather than searching all the normal locations.
+
+ 'library_dirs', if supplied, should be a list of directories to
+ search for libraries that were specified as bare library names
+ (ie. no directory component). These are on top of the system
+ default and those supplied to 'add_library_dir()' and/or
+ 'set_library_dirs()'. 'runtime_library_dirs' is a list of
+ directories that will be embedded into the shared library and used
+ to search for other shared libraries that *it* depends on at
+ run-time. (This may only be relevant on Unix.)
+
+ 'export_symbols' is a list of symbols that the shared library will
+ export. (This appears to be relevant only on Windows.)
+
+ 'debug' is as for 'compile()' and 'create_static_lib()', with the
+ slight distinction that it actually matters on most platforms (as
+ opposed to 'create_static_lib()', which includes a 'debug' flag
+ mostly for form's sake).
+
+ 'extra_preargs' and 'extra_postargs' are as for 'compile()' (except
+ of course that they supply command-line arguments for the
+ particular linker being used).
+
+ 'target_lang' is the target language for which the given objects
+ are being compiled. This allows specific linkage time treatment of
+ certain languages.
+
+ Raises LinkError on failure.
+ """
+ raise NotImplementedError
+
+ # Old 'link_*()' methods, rewritten to use the new 'link()' method.
+
+ def link_shared_lib(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_libname: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ self.link(
+ Compiler.SHARED_LIBRARY,
+ objects,
+ self.library_filename(output_libname, lib_type='shared'),
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ export_symbols,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )
+
+ def link_shared_object(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_filename: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ self.link(
+ Compiler.SHARED_OBJECT,
+ objects,
+ output_filename,
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ export_symbols,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )
+
+ def link_executable(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_progname: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: list[str] | None = None,
+ target_lang: str | None = None,
+ ):
+ self.link(
+ Compiler.EXECUTABLE,
+ objects,
+ self.executable_filename(output_progname),
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ None,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ None,
+ target_lang,
+ )
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function; there is
+ # no appropriate default implementation so subclasses should
+ # implement all of these.
+
+ def library_dir_option(self, dir: str) -> str:
+ """Return the compiler option to add 'dir' to the list of
+ directories searched for libraries.
+ """
+ raise NotImplementedError
+
+ def runtime_library_dir_option(self, dir: str) -> str:
+ """Return the compiler option to add 'dir' to the list of
+ directories searched for runtime libraries.
+ """
+ raise NotImplementedError
+
+ def library_option(self, lib: str) -> str:
+ """Return the compiler option to add 'lib' to the list of libraries
+ linked into the shared library or executable.
+ """
+ raise NotImplementedError
+
+ def has_function( # noqa: C901
+ self,
+ funcname: str,
+ includes: Iterable[str] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ libraries: list[str] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ ) -> bool:
+ """Return a boolean indicating whether funcname is provided as
+ a symbol on the current platform. The optional arguments can
+ be used to augment the compilation environment.
+
+ The libraries argument is a list of flags to be passed to the
+ linker to make additional symbol definitions available for
+ linking.
+
+ The includes and include_dirs arguments are deprecated.
+ Usually, supplying include files with function declarations
+ will cause function detection to fail even in cases where the
+ symbol is available for linking.
+
+ """
+ # this can't be included at module scope because it tries to
+ # import math which might not be available at that point - maybe
+ # the necessary logic should just be inlined?
+ import tempfile
+
+ if includes is None:
+ includes = []
+ else:
+ warnings.warn("includes is deprecated", DeprecationWarning)
+ if include_dirs is None:
+ include_dirs = []
+ else:
+ warnings.warn("include_dirs is deprecated", DeprecationWarning)
+ if libraries is None:
+ libraries = []
+ if library_dirs is None:
+ library_dirs = []
+ fd, fname = tempfile.mkstemp(".c", funcname, text=True)
+ with os.fdopen(fd, "w", encoding='utf-8') as f:
+ for incl in includes:
+ f.write(f"""#include "{incl}"\n""")
+ if not includes:
+ # Use "char func(void);" as the prototype to follow
+ # what autoconf does. This prototype does not match
+ # any well-known function the compiler might recognize
+ # as a builtin, so this ends up as a true link test.
+ # Without a fake prototype, the test would need to
+ # know the exact argument types, and the has_function
+ # interface does not provide that level of information.
+ f.write(
+ f"""\
+#ifdef __cplusplus
+extern "C"
+#endif
+char {funcname}(void);
+"""
+ )
+ f.write(
+ f"""\
+int main (int argc, char **argv) {{
+ {funcname}();
+ return 0;
+}}
+"""
+ )
+
+ try:
+ objects = self.compile([fname], include_dirs=include_dirs)
+ except CompileError:
+ return False
+ finally:
+ os.remove(fname)
+
+ try:
+ self.link_executable(
+ objects, "a.out", libraries=libraries, library_dirs=library_dirs
+ )
+ except (LinkError, TypeError):
+ return False
+ else:
+ os.remove(
+ self.executable_filename("a.out", output_dir=self.output_dir or '')
+ )
+ finally:
+ for fn in objects:
+ os.remove(fn)
+ return True
+
+ def find_library_file(
+ self, dirs: Iterable[str], lib: str, debug: bool = False
+ ) -> str | None:
+ """Search the specified list of directories for a static or shared
+ library file 'lib' and return the full path to that file. If
+ 'debug' true, look for a debugging version (if that makes sense on
+ the current platform). Return None if 'lib' wasn't found in any of
+ the specified directories.
+ """
+ raise NotImplementedError
+
+ # -- Filename generation methods -----------------------------------
+
+ # The default implementation of the filename generating methods are
+ # prejudiced towards the Unix/DOS/Windows view of the world:
+ # * object files are named by replacing the source file extension
+ # (eg. .c/.cpp -> .o/.obj)
+ # * library files (shared or static) are named by plugging the
+ # library name and extension into a format string, eg.
+ # "lib%s.%s" % (lib_name, ".a") for Unix static libraries
+ # * executables are named by appending an extension (possibly
+ # empty) to the program name: eg. progname + ".exe" for
+ # Windows
+ #
+ # To reduce redundant code, these methods expect to find
+ # several attributes in the current object (presumably defined
+ # as class attributes):
+ # * src_extensions -
+ # list of C/C++ source file extensions, eg. ['.c', '.cpp']
+ # * obj_extension -
+ # object file extension, eg. '.o' or '.obj'
+ # * static_lib_extension -
+ # extension for static library files, eg. '.a' or '.lib'
+ # * shared_lib_extension -
+ # extension for shared library/object files, eg. '.so', '.dll'
+ # * static_lib_format -
+ # format string for generating static library filenames,
+ # eg. 'lib%s.%s' or '%s.%s'
+ # * shared_lib_format
+ # format string for generating shared library filenames
+ # (probably same as static_lib_format, since the extension
+ # is one of the intended parameters to the format string)
+ # * exe_extension -
+ # extension for executable files, eg. '' or '.exe'
+
+ def object_filenames(
+ self,
+ source_filenames: Iterable[str | os.PathLike[str]],
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] | None = '',
+ ) -> list[str]:
+ if output_dir is None:
+ output_dir = ''
+ return list(
+ self._make_out_path(output_dir, strip_dir, src_name)
+ for src_name in source_filenames
+ )
+
+ @property
+ def out_extensions(self):
+ return dict.fromkeys(self.src_extensions, self.obj_extension)
+
+ def _make_out_path(self, output_dir, strip_dir, src_name):
+ return self._make_out_path_exts(
+ output_dir, strip_dir, src_name, self.out_extensions
+ )
+
+ @classmethod
+ def _make_out_path_exts(cls, output_dir, strip_dir, src_name, extensions):
+ r"""
+ >>> exts = {'.c': '.o'}
+ >>> Compiler._make_out_path_exts('.', False, '/foo/bar.c', exts).replace('\\', '/')
+ './foo/bar.o'
+ >>> Compiler._make_out_path_exts('.', True, '/foo/bar.c', exts).replace('\\', '/')
+ './bar.o'
+ """
+ src = pathlib.PurePath(src_name)
+ # Ensure base is relative to honor output_dir (python/cpython#37775).
+ base = cls._make_relative(src)
+ try:
+ new_ext = extensions[src.suffix]
+ except LookupError:
+ raise UnknownFileType(f"unknown file type '{src.suffix}' (from '{src}')")
+ if strip_dir:
+ base = pathlib.PurePath(base.name)
+ return os.path.join(output_dir, base.with_suffix(new_ext))
+
+ @staticmethod
+ def _make_relative(base: pathlib.Path):
+ return base.relative_to(base.anchor)
+
+ @overload
+ def shared_object_filename(
+ self,
+ basename: str,
+ strip_dir: Literal[False] = False,
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ @overload
+ def shared_object_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: Literal[True],
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ def shared_object_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] = '',
+ ) -> str:
+ assert output_dir is not None
+ if strip_dir:
+ basename = os.path.basename(basename)
+ return os.path.join(output_dir, basename + self.shared_lib_extension)
+
+ @overload
+ def executable_filename(
+ self,
+ basename: str,
+ strip_dir: Literal[False] = False,
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ @overload
+ def executable_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: Literal[True],
+ output_dir: str | os.PathLike[str] = "",
+ ) -> str: ...
+ def executable_filename(
+ self,
+ basename: str | os.PathLike[str],
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] = '',
+ ) -> str:
+ assert output_dir is not None
+ if strip_dir:
+ basename = os.path.basename(basename)
+ return os.path.join(output_dir, basename + (self.exe_extension or ''))
+
+ def library_filename(
+ self,
+ libname: str,
+ lib_type: str = "static",
+ strip_dir: bool = False,
+ output_dir: str | os.PathLike[str] = "", # or 'shared'
+ ):
+ assert output_dir is not None
+ expected = '"static", "shared", "dylib", "xcode_stub"'
+ if lib_type not in eval(expected):
+ raise ValueError(f"'lib_type' must be {expected}")
+ fmt = getattr(self, lib_type + "_lib_format")
+ ext = getattr(self, lib_type + "_lib_extension")
+
+ dir, base = os.path.split(libname)
+ filename = fmt % (base, ext)
+ if strip_dir:
+ dir = ''
+
+ return os.path.join(output_dir, dir, filename)
+
+ # -- Utility methods -----------------------------------------------
+
+ def announce(self, msg: object, level: int = 1) -> None:
+ log.debug(msg)
+
+ def debug_print(self, msg: object) -> None:
+ from distutils.debug import DEBUG
+
+ if DEBUG:
+ print(msg)
+
+ def warn(self, msg: object) -> None:
+ sys.stderr.write(f"warning: {msg}\n")
+
+ def execute(
+ self,
+ func: Callable[[Unpack[_Ts]], object],
+ args: tuple[Unpack[_Ts]],
+ msg: object = None,
+ level: int = 1,
+ ) -> None:
+ execute(func, args, msg, self.dry_run)
+
+ def spawn(
+ self, cmd: MutableSequence[bytes | str | os.PathLike[str]], **kwargs
+ ) -> None:
+ spawn(cmd, dry_run=self.dry_run, **kwargs)
+
+ @overload
+ def move_file(
+ self, src: str | os.PathLike[str], dst: _StrPathT
+ ) -> _StrPathT | str: ...
+ @overload
+ def move_file(
+ self, src: bytes | os.PathLike[bytes], dst: _BytesPathT
+ ) -> _BytesPathT | bytes: ...
+ def move_file(
+ self,
+ src: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ dst: str | os.PathLike[str] | bytes | os.PathLike[bytes],
+ ) -> str | os.PathLike[str] | bytes | os.PathLike[bytes]:
+ return move_file(src, dst, dry_run=self.dry_run)
+
+ def mkpath(self, name, mode=0o777):
+ mkpath(name, mode, dry_run=self.dry_run)
+
+
+# Map a sys.platform/os.name ('posix', 'nt') to the default compiler
+# type for that platform. Keys are interpreted as re match
+# patterns. Order is important; platform mappings are preferred over
+# OS names.
+_default_compilers = (
+ # Platform string mappings
+ # on a cygwin built python we can use gcc like an ordinary UNIXish
+ # compiler
+ ('cygwin.*', 'unix'),
+ ('zos', 'zos'),
+ # OS name mappings
+ ('posix', 'unix'),
+ ('nt', 'msvc'),
+)
+
+
+def get_default_compiler(osname: str | None = None, platform: str | None = None) -> str:
+ """Determine the default compiler to use for the given platform.
+
+ osname should be one of the standard Python OS names (i.e. the
+ ones returned by os.name) and platform the common value
+ returned by sys.platform for the platform in question.
+
+ The default values are os.name and sys.platform in case the
+ parameters are not given.
+ """
+ if osname is None:
+ osname = os.name
+ if platform is None:
+ platform = sys.platform
+ # Mingw is a special case where sys.platform is 'win32' but we
+ # want to use the 'mingw32' compiler, so check it first
+ if is_mingw():
+ return 'mingw32'
+ for pattern, compiler in _default_compilers:
+ if (
+ re.match(pattern, platform) is not None
+ or re.match(pattern, osname) is not None
+ ):
+ return compiler
+ # Default to Unix compiler
+ return 'unix'
+
+
+# Map compiler types to (module_name, class_name) pairs -- ie. where to
+# find the code that implements an interface to this compiler. (The module
+# is assumed to be in the 'distutils' package.)
+compiler_class = {
+ 'unix': ('unixccompiler', 'UnixCCompiler', "standard UNIX-style compiler"),
+ 'msvc': ('_msvccompiler', 'MSVCCompiler', "Microsoft Visual C++"),
+ 'cygwin': (
+ 'cygwinccompiler',
+ 'CygwinCCompiler',
+ "Cygwin port of GNU C Compiler for Win32",
+ ),
+ 'mingw32': (
+ 'cygwinccompiler',
+ 'Mingw32CCompiler',
+ "Mingw32 port of GNU C Compiler for Win32",
+ ),
+ 'bcpp': ('bcppcompiler', 'BCPPCompiler', "Borland C++ Compiler"),
+ 'zos': ('zosccompiler', 'zOSCCompiler', 'IBM XL C/C++ Compilers'),
+}
+
+
+def show_compilers() -> None:
+ """Print list of available compilers (used by the "--help-compiler"
+ options to "build", "build_ext", "build_clib").
+ """
+ # XXX this "knows" that the compiler option it's describing is
+ # "--compiler", which just happens to be the case for the three
+ # commands that use it.
+ from distutils.fancy_getopt import FancyGetopt
+
+ compilers = sorted(
+ ("compiler=" + compiler, None, compiler_class[compiler][2])
+ for compiler in compiler_class.keys()
+ )
+ pretty_printer = FancyGetopt(compilers)
+ pretty_printer.print_help("List of available compilers:")
+
+
+def new_compiler(
+ plat: str | None = None,
+ compiler: str | None = None,
+ verbose: bool = False,
+ dry_run: bool = False,
+ force: bool = False,
+) -> Compiler:
+ """Generate an instance of some CCompiler subclass for the supplied
+ platform/compiler combination. 'plat' defaults to 'os.name'
+ (eg. 'posix', 'nt'), and 'compiler' defaults to the default compiler
+ for that platform. Currently only 'posix' and 'nt' are supported, and
+ the default compilers are "traditional Unix interface" (UnixCCompiler
+ class) and Visual C++ (MSVCCompiler class). Note that it's perfectly
+ possible to ask for a Unix compiler object under Windows, and a
+ Microsoft compiler object under Unix -- if you supply a value for
+ 'compiler', 'plat' is ignored.
+ """
+ if plat is None:
+ plat = os.name
+
+ try:
+ if compiler is None:
+ compiler = get_default_compiler(plat)
+
+ (module_name, class_name, long_description) = compiler_class[compiler]
+ except KeyError:
+ msg = f"don't know how to compile C/C++ code on platform '{plat}'"
+ if compiler is not None:
+ msg = msg + f" with '{compiler}' compiler"
+ raise DistutilsPlatformError(msg)
+
+ try:
+ module_name = "distutils." + module_name
+ __import__(module_name)
+ module = sys.modules[module_name]
+ klass = vars(module)[class_name]
+ except ImportError:
+ raise DistutilsModuleError(
+ f"can't compile C/C++ code: unable to load module '{module_name}'"
+ )
+ except KeyError:
+ raise DistutilsModuleError(
+ f"can't compile C/C++ code: unable to find class '{class_name}' "
+ f"in module '{module_name}'"
+ )
+
+ # XXX The None is necessary to preserve backwards compatibility
+ # with classes that expect verbose to be the first positional
+ # argument.
+ return klass(None, dry_run, force)
+
+
+def gen_preprocess_options(
+ macros: Iterable[_Macro], include_dirs: Iterable[str]
+) -> list[str]:
+ """Generate C pre-processor options (-D, -U, -I) as used by at least
+ two types of compilers: the typical Unix compiler and Visual C++.
+ 'macros' is the usual thing, a list of 1- or 2-tuples, where (name,)
+ means undefine (-U) macro 'name', and (name,value) means define (-D)
+ macro 'name' to 'value'. 'include_dirs' is just a list of directory
+ names to be added to the header file search path (-I). Returns a list
+ of command-line options suitable for either Unix compilers or Visual
+ C++.
+ """
+ # XXX it would be nice (mainly aesthetic, and so we don't generate
+ # stupid-looking command lines) to go over 'macros' and eliminate
+ # redundant definitions/undefinitions (ie. ensure that only the
+ # latest mention of a particular macro winds up on the command
+ # line). I don't think it's essential, though, since most (all?)
+ # Unix C compilers only pay attention to the latest -D or -U
+ # mention of a macro on their command line. Similar situation for
+ # 'include_dirs'. I'm punting on both for now. Anyways, weeding out
+ # redundancies like this should probably be the province of
+ # CCompiler, since the data structures used are inherited from it
+ # and therefore common to all CCompiler classes.
+ pp_opts = []
+ for macro in macros:
+ if not (isinstance(macro, tuple) and 1 <= len(macro) <= 2):
+ raise TypeError(
+ f"bad macro definition '{macro}': "
+ "each element of 'macros' list must be a 1- or 2-tuple"
+ )
+
+ if len(macro) == 1: # undefine this macro
+ pp_opts.append(f"-U{macro[0]}")
+ elif len(macro) == 2:
+ if macro[1] is None: # define with no explicit value
+ pp_opts.append(f"-D{macro[0]}")
+ else:
+ # XXX *don't* need to be clever about quoting the
+ # macro value here, because we're going to avoid the
+ # shell at all costs when we spawn the command!
+ pp_opts.append("-D{}={}".format(*macro))
+
+ pp_opts.extend(f"-I{dir}" for dir in include_dirs)
+ return pp_opts
+
+
+def gen_lib_options(
+ compiler: Compiler,
+ library_dirs: Iterable[str],
+ runtime_library_dirs: Iterable[str],
+ libraries: Iterable[str],
+) -> list[str]:
+ """Generate linker options for searching library directories and
+ linking with specific libraries. 'libraries' and 'library_dirs' are,
+ respectively, lists of library names (not filenames!) and search
+ directories. Returns a list of command-line options suitable for use
+ with some compiler (depending on the two format strings passed in).
+ """
+ lib_opts = [compiler.library_dir_option(dir) for dir in library_dirs]
+
+ for dir in runtime_library_dirs:
+ lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir)))
+
+ # XXX it's important that we *not* remove redundant library mentions!
+ # sometimes you really do have to say "-lfoo -lbar -lfoo" in order to
+ # resolve all symbols. I just hope we never have to say "-lfoo obj.o
+ # -lbar" to get things to work -- that's certainly a possibility, but a
+ # pretty nasty way to arrange your C code.
+
+ for lib in libraries:
+ (lib_dir, lib_name) = os.path.split(lib)
+ if lib_dir:
+ lib_file = compiler.find_library_file([lib_dir], lib_name)
+ if lib_file:
+ lib_opts.append(lib_file)
+ else:
+ compiler.warn(
+ f"no library file corresponding to '{lib}' found (skipping)"
+ )
+ else:
+ lib_opts.append(compiler.library_option(lib))
+ return lib_opts
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py
new file mode 100644
index 00000000000..bfabbb306e7
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/cygwin.py
@@ -0,0 +1,340 @@
+"""distutils.cygwinccompiler
+
+Provides the CygwinCCompiler class, a subclass of UnixCCompiler that
+handles the Cygwin port of the GNU C compiler to Windows. It also contains
+the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
+cygwin in no-cygwin mode).
+"""
+
+import copy
+import os
+import pathlib
+import shlex
+import sys
+import warnings
+from subprocess import check_output
+
+from ...errors import (
+ DistutilsExecError,
+ DistutilsPlatformError,
+)
+from ...file_util import write_file
+from ...sysconfig import get_config_vars
+from ...version import LooseVersion, suppress_known_deprecation
+from . import unix
+from .errors import (
+ CompileError,
+ Error,
+)
+
+
+def get_msvcr():
+ """No longer needed, but kept for backward compatibility."""
+ return []
+
+
+_runtime_library_dirs_msg = (
+ "Unable to set runtime library search path on Windows, "
+ "usually indicated by `runtime_library_dirs` parameter to Extension"
+)
+
+
+class Compiler(unix.Compiler):
+ """Handles the Cygwin port of the GNU C compiler to Windows."""
+
+ compiler_type = 'cygwin'
+ obj_extension = ".o"
+ static_lib_extension = ".a"
+ shared_lib_extension = ".dll.a"
+ dylib_lib_extension = ".dll"
+ static_lib_format = "lib%s%s"
+ shared_lib_format = "lib%s%s"
+ dylib_lib_format = "cyg%s%s"
+ exe_extension = ".exe"
+
+ def __init__(self, verbose=False, dry_run=False, force=False):
+ super().__init__(verbose, dry_run, force)
+
+ status, details = check_config_h()
+ self.debug_print(f"Python's GCC status: {status} (details: {details})")
+ if status is not CONFIG_H_OK:
+ self.warn(
+ "Python's pyconfig.h doesn't seem to support your compiler. "
+ f"Reason: {details}. "
+ "Compiling may fail because of undefined preprocessor macros."
+ )
+
+ self.cc, self.cxx = get_config_vars('CC', 'CXX')
+
+ # Override 'CC' and 'CXX' environment variables for
+ # building using MINGW compiler for MSVC python.
+ self.cc = os.environ.get('CC', self.cc or 'gcc')
+ self.cxx = os.environ.get('CXX', self.cxx or 'g++')
+
+ self.linker_dll = self.cc
+ self.linker_dll_cxx = self.cxx
+ shared_option = "-shared"
+
+ self.set_executables(
+ compiler=f'{self.cc} -mcygwin -O -Wall',
+ compiler_so=f'{self.cc} -mcygwin -mdll -O -Wall',
+ compiler_cxx=f'{self.cxx} -mcygwin -O -Wall',
+ compiler_so_cxx=f'{self.cxx} -mcygwin -mdll -O -Wall',
+ linker_exe=f'{self.cc} -mcygwin',
+ linker_so=f'{self.linker_dll} -mcygwin {shared_option}',
+ linker_exe_cxx=f'{self.cxx} -mcygwin',
+ linker_so_cxx=f'{self.linker_dll_cxx} -mcygwin {shared_option}',
+ )
+
+ self.dll_libraries = get_msvcr()
+
+ @property
+ def gcc_version(self):
+ # Older numpy depended on this existing to check for ancient
+ # gcc versions. This doesn't make much sense with clang etc so
+ # just hardcode to something recent.
+ # https://github.com/numpy/numpy/pull/20333
+ warnings.warn(
+ "gcc_version attribute of CygwinCCompiler is deprecated. "
+ "Instead of returning actual gcc version a fixed value 11.2.0 is returned.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ with suppress_known_deprecation():
+ return LooseVersion("11.2.0")
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ """Compiles the source by spawning GCC and windres if needed."""
+ if ext in ('.rc', '.res'):
+ # gcc needs '.res' and '.rc' compiled to object files !!!
+ try:
+ self.spawn(["windres", "-i", src, "-o", obj])
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+ else: # for other files use the C-compiler
+ try:
+ if self.detect_language(src) == 'c++':
+ self.spawn(
+ self.compiler_so_cxx
+ + cc_args
+ + [src, '-o', obj]
+ + extra_postargs
+ )
+ else:
+ self.spawn(
+ self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs
+ )
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def link(
+ self,
+ target_desc,
+ objects,
+ output_filename,
+ output_dir=None,
+ libraries=None,
+ library_dirs=None,
+ runtime_library_dirs=None,
+ export_symbols=None,
+ debug=False,
+ extra_preargs=None,
+ extra_postargs=None,
+ build_temp=None,
+ target_lang=None,
+ ):
+ """Link the objects."""
+ # use separate copies, so we can modify the lists
+ extra_preargs = copy.copy(extra_preargs or [])
+ libraries = copy.copy(libraries or [])
+ objects = copy.copy(objects or [])
+
+ if runtime_library_dirs:
+ self.warn(_runtime_library_dirs_msg)
+
+ # Additional libraries
+ libraries.extend(self.dll_libraries)
+
+ # handle export symbols by creating a def-file
+ # with executables this only works with gcc/ld as linker
+ if (export_symbols is not None) and (
+ target_desc != self.EXECUTABLE or self.linker_dll == "gcc"
+ ):
+ # (The linker doesn't do anything if output is up-to-date.
+ # So it would probably better to check if we really need this,
+ # but for this we had to insert some unchanged parts of
+ # UnixCCompiler, and this is not what we want.)
+
+ # we want to put some files in the same directory as the
+ # object files are, build_temp doesn't help much
+ # where are the object files
+ temp_dir = os.path.dirname(objects[0])
+ # name of dll to give the helper files the same base name
+ (dll_name, dll_extension) = os.path.splitext(
+ os.path.basename(output_filename)
+ )
+
+ # generate the filenames for these files
+ def_file = os.path.join(temp_dir, dll_name + ".def")
+
+ # Generate .def file
+ contents = [f"LIBRARY {os.path.basename(output_filename)}", "EXPORTS"]
+ contents.extend(export_symbols)
+ self.execute(write_file, (def_file, contents), f"writing {def_file}")
+
+ # next add options for def-file
+
+ # for gcc/ld the def-file is specified as any object files
+ objects.append(def_file)
+
+ # end: if ((export_symbols is not None) and
+ # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
+
+ # who wants symbols and a many times larger output file
+ # should explicitly switch the debug mode on
+ # otherwise we let ld strip the output file
+ # (On my machine: 10KiB < stripped_file < ??100KiB
+ # unstripped_file = stripped_file + XXX KiB
+ # ( XXX=254 for a typical python extension))
+ if not debug:
+ extra_preargs.append("-s")
+
+ super().link(
+ target_desc,
+ objects,
+ output_filename,
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ None, # export_symbols, we do this in our def-file
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )
+
+ def runtime_library_dir_option(self, dir):
+ # cygwin doesn't support rpath. While in theory we could error
+ # out like MSVC does, code might expect it to work like on Unix, so
+ # just warn and hope for the best.
+ self.warn(_runtime_library_dirs_msg)
+ return []
+
+ # -- Miscellaneous methods -----------------------------------------
+
+ def _make_out_path(self, output_dir, strip_dir, src_name):
+ # use normcase to make sure '.rc' is really '.rc' and not '.RC'
+ norm_src_name = os.path.normcase(src_name)
+ return super()._make_out_path(output_dir, strip_dir, norm_src_name)
+
+ @property
+ def out_extensions(self):
+ """
+ Add support for rc and res files.
+ """
+ return {
+ **super().out_extensions,
+ **{ext: ext + self.obj_extension for ext in ('.res', '.rc')},
+ }
+
+
+# the same as cygwin plus some additional parameters
+class MinGW32Compiler(Compiler):
+ """Handles the Mingw32 port of the GNU C compiler to Windows."""
+
+ compiler_type = 'mingw32'
+
+ def __init__(self, verbose=False, dry_run=False, force=False):
+ super().__init__(verbose, dry_run, force)
+
+ shared_option = "-shared"
+
+ if is_cygwincc(self.cc):
+ raise Error('Cygwin gcc cannot be used with --compiler=mingw32')
+
+ self.set_executables(
+ compiler=f'{self.cc} -O -Wall',
+ compiler_so=f'{self.cc} -shared -O -Wall',
+ compiler_so_cxx=f'{self.cxx} -shared -O -Wall',
+ compiler_cxx=f'{self.cxx} -O -Wall',
+ linker_exe=f'{self.cc}',
+ linker_so=f'{self.linker_dll} {shared_option}',
+ linker_exe_cxx=f'{self.cxx}',
+ linker_so_cxx=f'{self.linker_dll_cxx} {shared_option}',
+ )
+
+ def runtime_library_dir_option(self, dir):
+ raise DistutilsPlatformError(_runtime_library_dirs_msg)
+
+
+# Because these compilers aren't configured in Python's pyconfig.h file by
+# default, we should at least warn the user if he is using an unmodified
+# version.
+
+CONFIG_H_OK = "ok"
+CONFIG_H_NOTOK = "not ok"
+CONFIG_H_UNCERTAIN = "uncertain"
+
+
+def check_config_h():
+ """Check if the current Python installation appears amenable to building
+ extensions with GCC.
+
+ Returns a tuple (status, details), where 'status' is one of the following
+ constants:
+
+ - CONFIG_H_OK: all is well, go ahead and compile
+ - CONFIG_H_NOTOK: doesn't look good
+ - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
+
+ 'details' is a human-readable string explaining the situation.
+
+ Note there are two ways to conclude "OK": either 'sys.version' contains
+ the string "GCC" (implying that this Python was built with GCC), or the
+ installed "pyconfig.h" contains the string "__GNUC__".
+ """
+
+ # XXX since this function also checks sys.version, it's not strictly a
+ # "pyconfig.h" check -- should probably be renamed...
+
+ from distutils import sysconfig
+
+ # if sys.version contains GCC then python was compiled with GCC, and the
+ # pyconfig.h file should be OK
+ if "GCC" in sys.version:
+ return CONFIG_H_OK, "sys.version mentions 'GCC'"
+
+ # Clang would also work
+ if "Clang" in sys.version:
+ return CONFIG_H_OK, "sys.version mentions 'Clang'"
+
+ # let's see if __GNUC__ is mentioned in python.h
+ fn = sysconfig.get_config_h_filename()
+ try:
+ config_h = pathlib.Path(fn).read_text(encoding='utf-8')
+ except OSError as exc:
+ return (CONFIG_H_UNCERTAIN, f"couldn't read '{fn}': {exc.strerror}")
+ else:
+ substring = '__GNUC__'
+ if substring in config_h:
+ code = CONFIG_H_OK
+ mention_inflected = 'mentions'
+ else:
+ code = CONFIG_H_NOTOK
+ mention_inflected = 'does not mention'
+ return code, f"{fn!r} {mention_inflected} {substring!r}"
+
+
+def is_cygwincc(cc):
+ """Try to determine if the compiler that would be used is from cygwin."""
+ out_string = check_output(shlex.split(cc) + ['-dumpmachine'])
+ return out_string.strip().endswith(b'cygwin')
+
+
+get_versions = None
+"""
+A stand-in for the previous get_versions() function to prevent failures
+when monkeypatched. See pypa/setuptools#2969.
+"""
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/errors.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/errors.py
new file mode 100644
index 00000000000..01328592b2d
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/errors.py
@@ -0,0 +1,24 @@
+class Error(Exception):
+ """Some compile/link operation failed."""
+
+
+class PreprocessError(Error):
+ """Failure to preprocess one or more C/C++ files."""
+
+
+class CompileError(Error):
+ """Failure to compile one or more C/C++ source files."""
+
+
+class LibError(Error):
+ """Failure to create a static library from one or more C/C++ object
+ files."""
+
+
+class LinkError(Error):
+ """Failure to link one or more C/C++ object files into an executable
+ or shared library file."""
+
+
+class UnknownFileType(Error):
+ """Attempt to process an unknown file type."""
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py
new file mode 100644
index 00000000000..6db062a9e71
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/msvc.py
@@ -0,0 +1,614 @@
+"""distutils._msvccompiler
+
+Contains MSVCCompiler, an implementation of the abstract CCompiler class
+for Microsoft Visual Studio 2015.
+
+This module requires VS 2015 or later.
+"""
+
+# Written by Perry Stoll
+# hacked by Robin Becker and Thomas Heller to do a better job of
+# finding DevStudio (through the registry)
+# ported to VS 2005 and VS 2008 by Christian Heimes
+# ported to VS 2015 by Steve Dower
+from __future__ import annotations
+
+import contextlib
+import os
+import subprocess
+import unittest.mock as mock
+import warnings
+from collections.abc import Iterable
+
+with contextlib.suppress(ImportError):
+ import winreg
+
+from itertools import count
+
+from ..._log import log
+from ...errors import (
+ DistutilsExecError,
+ DistutilsPlatformError,
+)
+from ...util import get_host_platform, get_platform
+from . import base
+from .base import gen_lib_options
+from .errors import (
+ CompileError,
+ LibError,
+ LinkError,
+)
+
+
+def _find_vc2015():
+ try:
+ key = winreg.OpenKeyEx(
+ winreg.HKEY_LOCAL_MACHINE,
+ r"Software\Microsoft\VisualStudio\SxS\VC7",
+ access=winreg.KEY_READ | winreg.KEY_WOW64_32KEY,
+ )
+ except OSError:
+ log.debug("Visual C++ is not registered")
+ return None, None
+
+ best_version = 0
+ best_dir = None
+ with key:
+ for i in count():
+ try:
+ v, vc_dir, vt = winreg.EnumValue(key, i)
+ except OSError:
+ break
+ if v and vt == winreg.REG_SZ and os.path.isdir(vc_dir):
+ try:
+ version = int(float(v))
+ except (ValueError, TypeError):
+ continue
+ if version >= 14 and version > best_version:
+ best_version, best_dir = version, vc_dir
+ return best_version, best_dir
+
+
+def _find_vc2017():
+ """Returns "15, path" based on the result of invoking vswhere.exe
+ If no install is found, returns "None, None"
+
+ The version is returned to avoid unnecessarily changing the function
+ result. It may be ignored when the path is not None.
+
+ If vswhere.exe is not available, by definition, VS 2017 is not
+ installed.
+ """
+ root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
+ if not root:
+ return None, None
+
+ variant = 'arm64' if get_platform() == 'win-arm64' else 'x86.x64'
+ suitable_components = (
+ f"Microsoft.VisualStudio.Component.VC.Tools.{variant}",
+ "Microsoft.VisualStudio.Workload.WDExpress",
+ )
+
+ for component in suitable_components:
+ # Workaround for `-requiresAny` (only available on VS 2017 > 15.6)
+ with contextlib.suppress(
+ subprocess.CalledProcessError, OSError, UnicodeDecodeError
+ ):
+ path = (
+ subprocess.check_output([
+ os.path.join(
+ root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
+ ),
+ "-latest",
+ "-prerelease",
+ "-requires",
+ component,
+ "-property",
+ "installationPath",
+ "-products",
+ "*",
+ ])
+ .decode(encoding="mbcs", errors="strict")
+ .strip()
+ )
+
+ path = os.path.join(path, "VC", "Auxiliary", "Build")
+ if os.path.isdir(path):
+ return 15, path
+
+ return None, None # no suitable component found
+
+
+PLAT_SPEC_TO_RUNTIME = {
+ 'x86': 'x86',
+ 'x86_amd64': 'x64',
+ 'x86_arm': 'arm',
+ 'x86_arm64': 'arm64',
+}
+
+
+def _find_vcvarsall(plat_spec):
+ # bpo-38597: Removed vcruntime return value
+ _, best_dir = _find_vc2017()
+
+ if not best_dir:
+ best_version, best_dir = _find_vc2015()
+
+ if not best_dir:
+ log.debug("No suitable Visual C++ version found")
+ return None, None
+
+ vcvarsall = os.path.join(best_dir, "vcvarsall.bat")
+ if not os.path.isfile(vcvarsall):
+ log.debug("%s cannot be found", vcvarsall)
+ return None, None
+
+ return vcvarsall, None
+
+
+def _get_vc_env(plat_spec):
+ if os.getenv("DISTUTILS_USE_SDK"):
+ return {key.lower(): value for key, value in os.environ.items()}
+
+ vcvarsall, _ = _find_vcvarsall(plat_spec)
+ if not vcvarsall:
+ raise DistutilsPlatformError(
+ 'Microsoft Visual C++ 14.0 or greater is required. '
+ 'Get it with "Microsoft C++ Build Tools": '
+ 'https://visualstudio.microsoft.com/visual-cpp-build-tools/'
+ )
+
+ try:
+ out = subprocess.check_output(
+ f'cmd /u /c "{vcvarsall}" {plat_spec} && set',
+ stderr=subprocess.STDOUT,
+ ).decode('utf-16le', errors='replace')
+ except subprocess.CalledProcessError as exc:
+ log.error(exc.output)
+ raise DistutilsPlatformError(f"Error executing {exc.cmd}")
+
+ env = {
+ key.lower(): value
+ for key, _, value in (line.partition('=') for line in out.splitlines())
+ if key and value
+ }
+
+ return env
+
+
+def _find_exe(exe, paths=None):
+ """Return path to an MSVC executable program.
+
+ Tries to find the program in several places: first, one of the
+ MSVC program search paths from the registry; next, the directories
+ in the PATH environment variable. If any of those work, return an
+ absolute path that is known to exist. If none of them work, just
+ return the original program name, 'exe'.
+ """
+ if not paths:
+ paths = os.getenv('path').split(os.pathsep)
+ for p in paths:
+ fn = os.path.join(os.path.abspath(p), exe)
+ if os.path.isfile(fn):
+ return fn
+ return exe
+
+
+_vcvars_names = {
+ 'win32': 'x86',
+ 'win-amd64': 'amd64',
+ 'win-arm32': 'arm',
+ 'win-arm64': 'arm64',
+}
+
+
+def _get_vcvars_spec(host_platform, platform):
+ """
+ Given a host platform and platform, determine the spec for vcvarsall.
+
+ Uses the native MSVC host if the host platform would need expensive
+ emulation for x86.
+
+ >>> _get_vcvars_spec('win-arm64', 'win32')
+ 'arm64_x86'
+ >>> _get_vcvars_spec('win-arm64', 'win-amd64')
+ 'arm64_amd64'
+
+ Otherwise, always cross-compile from x86 to work with the
+ lighter-weight MSVC installs that do not include native 64-bit tools.
+
+ >>> _get_vcvars_spec('win32', 'win32')
+ 'x86'
+ >>> _get_vcvars_spec('win-arm32', 'win-arm32')
+ 'x86_arm'
+ >>> _get_vcvars_spec('win-amd64', 'win-arm64')
+ 'x86_arm64'
+ """
+ if host_platform != 'win-arm64':
+ host_platform = 'win32'
+ vc_hp = _vcvars_names[host_platform]
+ vc_plat = _vcvars_names[platform]
+ return vc_hp if vc_hp == vc_plat else f'{vc_hp}_{vc_plat}'
+
+
+class Compiler(base.Compiler):
+ """Concrete class that implements an interface to Microsoft Visual C++,
+ as defined by the CCompiler abstract class."""
+
+ compiler_type = 'msvc'
+
+ # Just set this so CCompiler's constructor doesn't barf. We currently
+ # don't use the 'set_executables()' bureaucracy provided by CCompiler,
+ # as it really isn't necessary for this sort of single-compiler class.
+ # Would be nice to have a consistent interface with UnixCCompiler,
+ # though, so it's worth thinking about.
+ executables = {}
+
+ # Private class data (need to distinguish C from C++ source for compiler)
+ _c_extensions = ['.c']
+ _cpp_extensions = ['.cc', '.cpp', '.cxx']
+ _rc_extensions = ['.rc']
+ _mc_extensions = ['.mc']
+
+ # Needed for the filename generation methods provided by the
+ # base class, CCompiler.
+ src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
+ res_extension = '.res'
+ obj_extension = '.obj'
+ static_lib_extension = '.lib'
+ shared_lib_extension = '.dll'
+ static_lib_format = shared_lib_format = '%s%s'
+ exe_extension = '.exe'
+
+ def __init__(self, verbose=False, dry_run=False, force=False) -> None:
+ super().__init__(verbose, dry_run, force)
+ # target platform (.plat_name is consistent with 'bdist')
+ self.plat_name = None
+ self.initialized = False
+
+ @classmethod
+ def _configure(cls, vc_env):
+ """
+ Set class-level include/lib dirs.
+ """
+ cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
+ cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
+
+ @staticmethod
+ def _parse_path(val):
+ return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
+
+ def initialize(self, plat_name: str | None = None) -> None:
+ # multi-init means we would need to check platform same each time...
+ assert not self.initialized, "don't init multiple times"
+ if plat_name is None:
+ plat_name = get_platform()
+ # sanity check for platforms to prevent obscure errors later.
+ if plat_name not in _vcvars_names:
+ raise DistutilsPlatformError(
+ f"--plat-name must be one of {tuple(_vcvars_names)}"
+ )
+
+ plat_spec = _get_vcvars_spec(get_host_platform(), plat_name)
+
+ vc_env = _get_vc_env(plat_spec)
+ if not vc_env:
+ raise DistutilsPlatformError(
+ "Unable to find a compatible Visual Studio installation."
+ )
+ self._configure(vc_env)
+
+ self._paths = vc_env.get('path', '')
+ paths = self._paths.split(os.pathsep)
+ self.cc = _find_exe("cl.exe", paths)
+ self.linker = _find_exe("link.exe", paths)
+ self.lib = _find_exe("lib.exe", paths)
+ self.rc = _find_exe("rc.exe", paths) # resource compiler
+ self.mc = _find_exe("mc.exe", paths) # message compiler
+ self.mt = _find_exe("mt.exe", paths) # message compiler
+
+ self.preprocess_options = None
+ # bpo-38597: Always compile with dynamic linking
+ # Future releases of Python 3.x will include all past
+ # versions of vcruntime*.dll for compatibility.
+ self.compile_options = ['/nologo', '/O2', '/W3', '/GL', '/DNDEBUG', '/MD']
+
+ self.compile_options_debug = [
+ '/nologo',
+ '/Od',
+ '/MDd',
+ '/Zi',
+ '/W3',
+ '/D_DEBUG',
+ ]
+
+ ldflags = ['/nologo', '/INCREMENTAL:NO', '/LTCG']
+
+ ldflags_debug = ['/nologo', '/INCREMENTAL:NO', '/LTCG', '/DEBUG:FULL']
+
+ self.ldflags_exe = [*ldflags, '/MANIFEST:EMBED,ID=1']
+ self.ldflags_exe_debug = [*ldflags_debug, '/MANIFEST:EMBED,ID=1']
+ self.ldflags_shared = [
+ *ldflags,
+ '/DLL',
+ '/MANIFEST:EMBED,ID=2',
+ '/MANIFESTUAC:NO',
+ ]
+ self.ldflags_shared_debug = [
+ *ldflags_debug,
+ '/DLL',
+ '/MANIFEST:EMBED,ID=2',
+ '/MANIFESTUAC:NO',
+ ]
+ self.ldflags_static = [*ldflags]
+ self.ldflags_static_debug = [*ldflags_debug]
+
+ self._ldflags = {
+ (base.Compiler.EXECUTABLE, None): self.ldflags_exe,
+ (base.Compiler.EXECUTABLE, False): self.ldflags_exe,
+ (base.Compiler.EXECUTABLE, True): self.ldflags_exe_debug,
+ (base.Compiler.SHARED_OBJECT, None): self.ldflags_shared,
+ (base.Compiler.SHARED_OBJECT, False): self.ldflags_shared,
+ (base.Compiler.SHARED_OBJECT, True): self.ldflags_shared_debug,
+ (base.Compiler.SHARED_LIBRARY, None): self.ldflags_static,
+ (base.Compiler.SHARED_LIBRARY, False): self.ldflags_static,
+ (base.Compiler.SHARED_LIBRARY, True): self.ldflags_static_debug,
+ }
+
+ self.initialized = True
+
+ # -- Worker methods ------------------------------------------------
+
+ @property
+ def out_extensions(self) -> dict[str, str]:
+ return {
+ **super().out_extensions,
+ **{
+ ext: self.res_extension
+ for ext in self._rc_extensions + self._mc_extensions
+ },
+ }
+
+ def compile( # noqa: C901
+ self,
+ sources,
+ output_dir=None,
+ macros=None,
+ include_dirs=None,
+ debug=False,
+ extra_preargs=None,
+ extra_postargs=None,
+ depends=None,
+ ):
+ if not self.initialized:
+ self.initialize()
+ compile_info = self._setup_compile(
+ output_dir, macros, include_dirs, sources, depends, extra_postargs
+ )
+ macros, objects, extra_postargs, pp_opts, build = compile_info
+
+ compile_opts = extra_preargs or []
+ compile_opts.append('/c')
+ if debug:
+ compile_opts.extend(self.compile_options_debug)
+ else:
+ compile_opts.extend(self.compile_options)
+
+ add_cpp_opts = False
+
+ for obj in objects:
+ try:
+ src, ext = build[obj]
+ except KeyError:
+ continue
+ if debug:
+ # pass the full pathname to MSVC in debug mode,
+ # this allows the debugger to find the source file
+ # without asking the user to browse for it
+ src = os.path.abspath(src)
+
+ if ext in self._c_extensions:
+ input_opt = f"/Tc{src}"
+ elif ext in self._cpp_extensions:
+ input_opt = f"/Tp{src}"
+ add_cpp_opts = True
+ elif ext in self._rc_extensions:
+ # compile .RC to .RES file
+ input_opt = src
+ output_opt = "/fo" + obj
+ try:
+ self.spawn([self.rc] + pp_opts + [output_opt, input_opt])
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+ continue
+ elif ext in self._mc_extensions:
+ # Compile .MC to .RC file to .RES file.
+ # * '-h dir' specifies the directory for the
+ # generated include file
+ # * '-r dir' specifies the target directory of the
+ # generated RC file and the binary message resource
+ # it includes
+ #
+ # For now (since there are no options to change this),
+ # we use the source-directory for the include file and
+ # the build directory for the RC file and message
+ # resources. This works at least for win32all.
+ h_dir = os.path.dirname(src)
+ rc_dir = os.path.dirname(obj)
+ try:
+ # first compile .MC to .RC and .H file
+ self.spawn([self.mc, '-h', h_dir, '-r', rc_dir, src])
+ base, _ = os.path.splitext(os.path.basename(src))
+ rc_file = os.path.join(rc_dir, base + '.rc')
+ # then compile .RC to .RES file
+ self.spawn([self.rc, "/fo" + obj, rc_file])
+
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+ continue
+ else:
+ # how to handle this file?
+ raise CompileError(f"Don't know how to compile {src} to {obj}")
+
+ args = [self.cc] + compile_opts + pp_opts
+ if add_cpp_opts:
+ args.append('/EHsc')
+ args.extend((input_opt, "/Fo" + obj))
+ args.extend(extra_postargs)
+
+ try:
+ self.spawn(args)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ return objects
+
+ def create_static_lib(
+ self,
+ objects: list[str] | tuple[str, ...],
+ output_libname: str,
+ output_dir: str | None = None,
+ debug: bool = False,
+ target_lang: str | None = None,
+ ) -> None:
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ output_filename = self.library_filename(output_libname, output_dir=output_dir)
+
+ if self._need_link(objects, output_filename):
+ lib_args = objects + ['/OUT:' + output_filename]
+ if debug:
+ pass # XXX what goes here?
+ try:
+ log.debug('Executing "%s" %s', self.lib, ' '.join(lib_args))
+ self.spawn([self.lib] + lib_args)
+ except DistutilsExecError as msg:
+ raise LibError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+ def link(
+ self,
+ target_desc: str,
+ objects: list[str] | tuple[str, ...],
+ output_filename: str,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols: Iterable[str] | None = None,
+ debug: bool = False,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: Iterable[str] | None = None,
+ build_temp: str | os.PathLike[str] | None = None,
+ target_lang: str | None = None,
+ ) -> None:
+ if not self.initialized:
+ self.initialize()
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+ libraries, library_dirs, runtime_library_dirs = fixed_args
+
+ if runtime_library_dirs:
+ self.warn(
+ "I don't know what to do with 'runtime_library_dirs': "
+ + str(runtime_library_dirs)
+ )
+
+ lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+ ldflags = self._ldflags[target_desc, debug]
+
+ export_opts = ["/EXPORT:" + sym for sym in (export_symbols or [])]
+
+ ld_args = (
+ ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
+ )
+
+ # The MSVC linker generates .lib and .exp files, which cannot be
+ # suppressed by any linker switches. The .lib files may even be
+ # needed! Make sure they are generated in the temporary build
+ # directory. Since they have different names for debug and release
+ # builds, they can go into the same directory.
+ build_temp = os.path.dirname(objects[0])
+ if export_symbols is not None:
+ (dll_name, dll_ext) = os.path.splitext(
+ os.path.basename(output_filename)
+ )
+ implib_file = os.path.join(build_temp, self.library_filename(dll_name))
+ ld_args.append('/IMPLIB:' + implib_file)
+
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+
+ output_dir = os.path.dirname(os.path.abspath(output_filename))
+ self.mkpath(output_dir)
+ try:
+ log.debug('Executing "%s" %s', self.linker, ' '.join(ld_args))
+ self.spawn([self.linker] + ld_args)
+ except DistutilsExecError as msg:
+ raise LinkError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+ def spawn(self, cmd):
+ env = dict(os.environ, PATH=self._paths)
+ with self._fallback_spawn(cmd, env) as fallback:
+ return super().spawn(cmd, env=env)
+ return fallback.value
+
+ @contextlib.contextmanager
+ def _fallback_spawn(self, cmd, env):
+ """
+ Discovered in pypa/distutils#15, some tools monkeypatch the compiler,
+ so the 'env' kwarg causes a TypeError. Detect this condition and
+ restore the legacy, unsafe behavior.
+ """
+ bag = type('Bag', (), {})()
+ try:
+ yield bag
+ except TypeError as exc:
+ if "unexpected keyword argument 'env'" not in str(exc):
+ raise
+ else:
+ return
+ warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
+ with mock.patch.dict('os.environ', env):
+ bag.value = super().spawn(cmd)
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
+ def library_dir_option(self, dir):
+ return "/LIBPATH:" + dir
+
+ def runtime_library_dir_option(self, dir):
+ raise DistutilsPlatformError(
+ "don't know how to set runtime library search path for MSVC"
+ )
+
+ def library_option(self, lib):
+ return self.library_filename(lib)
+
+ def find_library_file(self, dirs, lib, debug=False):
+ # Prefer a debugging library if found (and requested), but deal
+ # with it if we don't have one.
+ if debug:
+ try_names = [lib + "_d", lib]
+ else:
+ try_names = [lib]
+ for dir in dirs:
+ for name in try_names:
+ libfile = os.path.join(dir, self.library_filename(name))
+ if os.path.isfile(libfile):
+ return libfile
+ else:
+ # Oops, didn't find it in *any* of 'dirs'
+ return None
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/unix.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/unix.py
new file mode 100644
index 00000000000..1231b32d20f
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/unix.py
@@ -0,0 +1,422 @@
+"""distutils.unixccompiler
+
+Contains the UnixCCompiler class, a subclass of CCompiler that handles
+the "typical" Unix-style command-line C compiler:
+ * macros defined with -Dname[=value]
+ * macros undefined with -Uname
+ * include search directories specified with -Idir
+ * libraries specified with -lllib
+ * library search directories specified with -Ldir
+ * compile handled by 'cc' (or similar) executable with -c option:
+ compiles .c to .o
+ * link static library handled by 'ar' command (possibly with 'ranlib')
+ * link shared library handled by 'cc -shared'
+"""
+
+from __future__ import annotations
+
+import itertools
+import os
+import re
+import shlex
+import sys
+from collections.abc import Iterable
+
+from ... import sysconfig
+from ..._log import log
+from ..._macos_compat import compiler_fixup
+from ..._modified import newer
+from ...compat import consolidate_linker_args
+from ...errors import DistutilsExecError
+from . import base
+from .base import _Macro, gen_lib_options, gen_preprocess_options
+from .errors import (
+ CompileError,
+ LibError,
+ LinkError,
+)
+
+# XXX Things not currently handled:
+# * optimization/debug/warning flags; we just use whatever's in Python's
+# Makefile and live with it. Is this adequate? If not, we might
+# have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
+# SunCCompiler, and I suspect down that road lies madness.
+# * even if we don't know a warning flag from an optimization flag,
+# we need some way for outsiders to feed preprocessor/compiler/linker
+# flags in to us -- eg. a sysadmin might want to mandate certain flags
+# via a site config file, or a user might want to set something for
+# compiling this module distribution only via the setup.py command
+# line, whatever. As long as these options come from something on the
+# current system, they can be as system-dependent as they like, and we
+# should just happily stuff them into the preprocessor/compiler/linker
+# options and carry on.
+
+
+def _split_env(cmd):
+ """
+ For macOS, split command into 'env' portion (if any)
+ and the rest of the linker command.
+
+ >>> _split_env(['a', 'b', 'c'])
+ ([], ['a', 'b', 'c'])
+ >>> _split_env(['/usr/bin/env', 'A=3', 'gcc'])
+ (['/usr/bin/env', 'A=3'], ['gcc'])
+ """
+ pivot = 0
+ if os.path.basename(cmd[0]) == "env":
+ pivot = 1
+ while '=' in cmd[pivot]:
+ pivot += 1
+ return cmd[:pivot], cmd[pivot:]
+
+
+def _split_aix(cmd):
+ """
+ AIX platforms prefix the compiler with the ld_so_aix
+ script, so split that from the linker command.
+
+ >>> _split_aix(['a', 'b', 'c'])
+ ([], ['a', 'b', 'c'])
+ >>> _split_aix(['/bin/foo/ld_so_aix', 'gcc'])
+ (['/bin/foo/ld_so_aix'], ['gcc'])
+ """
+ pivot = os.path.basename(cmd[0]) == 'ld_so_aix'
+ return cmd[:pivot], cmd[pivot:]
+
+
+def _linker_params(linker_cmd, compiler_cmd):
+ """
+ The linker command usually begins with the compiler
+ command (possibly multiple elements), followed by zero or more
+ params for shared library building.
+
+ If the LDSHARED env variable overrides the linker command,
+ however, the commands may not match.
+
+ Return the best guess of the linker parameters by stripping
+ the linker command. If the compiler command does not
+ match the linker command, assume the linker command is
+ just the first element.
+
+ >>> _linker_params('gcc foo bar'.split(), ['gcc'])
+ ['foo', 'bar']
+ >>> _linker_params('gcc foo bar'.split(), ['other'])
+ ['foo', 'bar']
+ >>> _linker_params('ccache gcc foo bar'.split(), 'ccache gcc'.split())
+ ['foo', 'bar']
+ >>> _linker_params(['gcc'], ['gcc'])
+ []
+ """
+ c_len = len(compiler_cmd)
+ pivot = c_len if linker_cmd[:c_len] == compiler_cmd else 1
+ return linker_cmd[pivot:]
+
+
+class Compiler(base.Compiler):
+ compiler_type = 'unix'
+
+ # These are used by CCompiler in two places: the constructor sets
+ # instance attributes 'preprocessor', 'compiler', etc. from them, and
+ # 'set_executable()' allows any of these to be set. The defaults here
+ # are pretty generic; they will probably have to be set by an outsider
+ # (eg. using information discovered by the sysconfig about building
+ # Python extensions).
+ executables = {
+ 'preprocessor': None,
+ 'compiler': ["cc"],
+ 'compiler_so': ["cc"],
+ 'compiler_cxx': ["c++"],
+ 'compiler_so_cxx': ["c++"],
+ 'linker_so': ["cc", "-shared"],
+ 'linker_so_cxx': ["c++", "-shared"],
+ 'linker_exe': ["cc"],
+ 'linker_exe_cxx': ["c++", "-shared"],
+ 'archiver': ["ar", "-cr"],
+ 'ranlib': None,
+ }
+
+ if sys.platform[:6] == "darwin":
+ executables['ranlib'] = ["ranlib"]
+
+ # Needed for the filename generation methods provided by the base
+ # class, CCompiler. NB. whoever instantiates/uses a particular
+ # UnixCCompiler instance should set 'shared_lib_ext' -- we set a
+ # reasonable common default here, but it's not necessarily used on all
+ # Unices!
+
+ src_extensions = [".c", ".C", ".cc", ".cxx", ".cpp", ".m"]
+ obj_extension = ".o"
+ static_lib_extension = ".a"
+ shared_lib_extension = ".so"
+ dylib_lib_extension = ".dylib"
+ xcode_stub_lib_extension = ".tbd"
+ static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
+ xcode_stub_lib_format = dylib_lib_format
+ if sys.platform == "cygwin":
+ exe_extension = ".exe"
+ shared_lib_extension = ".dll.a"
+ dylib_lib_extension = ".dll"
+ dylib_lib_format = "cyg%s%s"
+
+ def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
+ """Remove standard library path from rpath"""
+ libraries, library_dirs, runtime_library_dirs = super()._fix_lib_args(
+ libraries, library_dirs, runtime_library_dirs
+ )
+ libdir = sysconfig.get_config_var('LIBDIR')
+ if (
+ runtime_library_dirs
+ and libdir.startswith("/usr/lib")
+ and (libdir in runtime_library_dirs)
+ ):
+ runtime_library_dirs.remove(libdir)
+ return libraries, library_dirs, runtime_library_dirs
+
+ def preprocess(
+ self,
+ source: str | os.PathLike[str],
+ output_file: str | os.PathLike[str] | None = None,
+ macros: list[_Macro] | None = None,
+ include_dirs: list[str] | tuple[str, ...] | None = None,
+ extra_preargs: list[str] | None = None,
+ extra_postargs: Iterable[str] | None = None,
+ ):
+ fixed_args = self._fix_compile_args(None, macros, include_dirs)
+ ignore, macros, include_dirs = fixed_args
+ pp_opts = gen_preprocess_options(macros, include_dirs)
+ pp_args = self.preprocessor + pp_opts
+ if output_file:
+ pp_args.extend(['-o', output_file])
+ if extra_preargs:
+ pp_args[:0] = extra_preargs
+ if extra_postargs:
+ pp_args.extend(extra_postargs)
+ pp_args.append(source)
+
+ # reasons to preprocess:
+ # - force is indicated
+ # - output is directed to stdout
+ # - source file is newer than the target
+ preprocess = self.force or output_file is None or newer(source, output_file)
+ if not preprocess:
+ return
+
+ if output_file:
+ self.mkpath(os.path.dirname(output_file))
+
+ try:
+ self.spawn(pp_args)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ compiler_so = compiler_fixup(self.compiler_so, cc_args + extra_postargs)
+ compiler_so_cxx = compiler_fixup(self.compiler_so_cxx, cc_args + extra_postargs)
+ try:
+ if self.detect_language(src) == 'c++':
+ self.spawn(
+ compiler_so_cxx + cc_args + [src, '-o', obj] + extra_postargs
+ )
+ else:
+ self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def create_static_lib(
+ self, objects, output_libname, output_dir=None, debug=False, target_lang=None
+ ):
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+
+ output_filename = self.library_filename(output_libname, output_dir=output_dir)
+
+ if self._need_link(objects, output_filename):
+ self.mkpath(os.path.dirname(output_filename))
+ self.spawn(self.archiver + [output_filename] + objects + self.objects)
+
+ # Not many Unices required ranlib anymore -- SunOS 4.x is, I
+ # think the only major Unix that does. Maybe we need some
+ # platform intelligence here to skip ranlib if it's not
+ # needed -- or maybe Python's configure script took care of
+ # it for us, hence the check for leading colon.
+ if self.ranlib:
+ try:
+ self.spawn(self.ranlib + [output_filename])
+ except DistutilsExecError as msg:
+ raise LibError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+ def link(
+ self,
+ target_desc,
+ objects: list[str] | tuple[str, ...],
+ output_filename,
+ output_dir: str | None = None,
+ libraries: list[str] | tuple[str, ...] | None = None,
+ library_dirs: list[str] | tuple[str, ...] | None = None,
+ runtime_library_dirs: list[str] | tuple[str, ...] | None = None,
+ export_symbols=None,
+ debug=False,
+ extra_preargs=None,
+ extra_postargs=None,
+ build_temp=None,
+ target_lang=None,
+ ):
+ objects, output_dir = self._fix_object_args(objects, output_dir)
+ fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
+ libraries, library_dirs, runtime_library_dirs = fixed_args
+
+ lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
+ if not isinstance(output_dir, (str, type(None))):
+ raise TypeError("'output_dir' must be a string or None")
+ if output_dir is not None:
+ output_filename = os.path.join(output_dir, output_filename)
+
+ if self._need_link(objects, output_filename):
+ ld_args = objects + self.objects + lib_opts + ['-o', output_filename]
+ if debug:
+ ld_args[:0] = ['-g']
+ if extra_preargs:
+ ld_args[:0] = extra_preargs
+ if extra_postargs:
+ ld_args.extend(extra_postargs)
+ self.mkpath(os.path.dirname(output_filename))
+ try:
+ # Select a linker based on context: linker_exe when
+ # building an executable or linker_so (with shared options)
+ # when building a shared library.
+ building_exe = target_desc == base.Compiler.EXECUTABLE
+ target_cxx = target_lang == "c++"
+ linker = (
+ (self.linker_exe_cxx if target_cxx else self.linker_exe)
+ if building_exe
+ else (self.linker_so_cxx if target_cxx else self.linker_so)
+ )[:]
+
+ if target_cxx and self.compiler_cxx:
+ env, linker_ne = _split_env(linker)
+ aix, linker_na = _split_aix(linker_ne)
+ _, compiler_cxx_ne = _split_env(self.compiler_cxx)
+ _, linker_exe_ne = _split_env(self.linker_exe_cxx)
+
+ params = _linker_params(linker_na, linker_exe_ne)
+ linker = env + aix + compiler_cxx_ne + params
+
+ linker = compiler_fixup(linker, ld_args)
+
+ self.spawn(linker + ld_args)
+ except DistutilsExecError as msg:
+ raise LinkError(msg)
+ else:
+ log.debug("skipping %s (up-to-date)", output_filename)
+
+ # -- Miscellaneous methods -----------------------------------------
+ # These are all used by the 'gen_lib_options() function, in
+ # ccompiler.py.
+
+ def library_dir_option(self, dir):
+ return "-L" + dir
+
+ def _is_gcc(self):
+ cc_var = sysconfig.get_config_var("CC")
+ compiler = os.path.basename(shlex.split(cc_var)[0])
+ return "gcc" in compiler or "g++" in compiler
+
+ def runtime_library_dir_option(self, dir: str) -> str | list[str]: # type: ignore[override] # Fixed in pypa/distutils#339
+ # XXX Hackish, at the very least. See Python bug #445902:
+ # https://bugs.python.org/issue445902
+ # Linkers on different platforms need different options to
+ # specify that directories need to be added to the list of
+ # directories searched for dependencies when a dynamic library
+ # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to
+ # be told to pass the -R option through to the linker, whereas
+ # other compilers and gcc on other systems just know this.
+ # Other compilers may need something slightly different. At
+ # this time, there's no way to determine this information from
+ # the configuration data stored in the Python installation, so
+ # we use this hack.
+ if sys.platform[:6] == "darwin":
+ from distutils.util import get_macosx_target_ver, split_version
+
+ macosx_target_ver = get_macosx_target_ver()
+ if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]:
+ return "-Wl,-rpath," + dir
+ else: # no support for -rpath on earlier macOS versions
+ return "-L" + dir
+ elif sys.platform[:7] == "freebsd":
+ return "-Wl,-rpath=" + dir
+ elif sys.platform[:5] == "hp-ux":
+ return [
+ "-Wl,+s" if self._is_gcc() else "+s",
+ "-L" + dir,
+ ]
+
+ # For all compilers, `-Wl` is the presumed way to pass a
+ # compiler option to the linker
+ if sysconfig.get_config_var("GNULD") == "yes":
+ return consolidate_linker_args([
+ # Force RUNPATH instead of RPATH
+ "-Wl,--enable-new-dtags",
+ "-Wl,-rpath," + dir,
+ ])
+ else:
+ return "-Wl,-R" + dir
+
+ def library_option(self, lib):
+ return "-l" + lib
+
+ @staticmethod
+ def _library_root(dir):
+ """
+ macOS users can specify an alternate SDK using'-isysroot'.
+ Calculate the SDK root if it is specified.
+
+ Note that, as of Xcode 7, Apple SDKs may contain textual stub
+ libraries with .tbd extensions rather than the normal .dylib
+ shared libraries installed in /. The Apple compiler tool
+ chain handles this transparently but it can cause problems
+ for programs that are being built with an SDK and searching
+ for specific libraries. Callers of find_library_file need to
+ keep in mind that the base filename of the returned SDK library
+ file might have a different extension from that of the library
+ file installed on the running system, for example:
+ /Applications/Xcode.app/Contents/Developer/Platforms/
+ MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
+ usr/lib/libedit.tbd
+ vs
+ /usr/lib/libedit.dylib
+ """
+ cflags = sysconfig.get_config_var('CFLAGS')
+ match = re.search(r'-isysroot\s*(\S+)', cflags)
+
+ apply_root = (
+ sys.platform == 'darwin'
+ and match
+ and (
+ dir.startswith('/System/')
+ or (dir.startswith('/usr/') and not dir.startswith('/usr/local/'))
+ )
+ )
+
+ return os.path.join(match.group(1), dir[1:]) if apply_root else dir
+
+ def find_library_file(self, dirs, lib, debug=False):
+ """
+ Second-guess the linker with not much hard
+ data to go on: GCC seems to prefer the shared library, so
+ assume that *all* Unix C compilers do,
+ ignoring even GCC's "-static" option.
+ """
+ lib_names = (
+ self.library_filename(lib, lib_type=type)
+ for type in 'dylib xcode_stub shared static'.split()
+ )
+
+ roots = map(self._library_root, dirs)
+
+ searched = itertools.starmap(os.path.join, itertools.product(roots, lib_names))
+
+ found = filter(os.path.exists, searched)
+
+ # Return None if it could not be found in any dir.
+ return next(found, None)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py
new file mode 100644
index 00000000000..82d017fc90d
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/C/zos.py
@@ -0,0 +1,230 @@
+"""distutils.zosccompiler
+
+Contains the selection of the c & c++ compilers on z/OS. There are several
+different c compilers on z/OS, all of them are optional, so the correct
+one needs to be chosen based on the users input. This is compatible with
+the following compilers:
+
+IBM C/C++ For Open Enterprise Languages on z/OS 2.0
+IBM Open XL C/C++ 1.1 for z/OS
+IBM XL C/C++ V2.4.1 for z/OS 2.4 and 2.5
+IBM z/OS XL C/C++
+"""
+
+import os
+
+from ... import sysconfig
+from ...errors import DistutilsExecError
+from . import unix
+from .errors import CompileError
+
+_cc_args = {
+ 'ibm-openxl': [
+ '-m64',
+ '-fvisibility=default',
+ '-fzos-le-char-mode=ascii',
+ '-fno-short-enums',
+ ],
+ 'ibm-xlclang': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ ],
+ 'ibm-xlc': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ '-qlanglvl=extc99',
+ ],
+}
+
+_cxx_args = {
+ 'ibm-openxl': [
+ '-m64',
+ '-fvisibility=default',
+ '-fzos-le-char-mode=ascii',
+ '-fno-short-enums',
+ ],
+ 'ibm-xlclang': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ ],
+ 'ibm-xlc': [
+ '-q64',
+ '-qexportall',
+ '-qascii',
+ '-qstrict',
+ '-qnocsect',
+ '-Wa,asa,goff',
+ '-Wa,xplink',
+ '-qgonumber',
+ '-qenum=int',
+ '-Wc,DLL',
+ '-qlanglvl=extended0x',
+ ],
+}
+
+_asm_args = {
+ 'ibm-openxl': ['-fasm', '-fno-integrated-as', '-Wa,--ASA', '-Wa,--GOFF'],
+ 'ibm-xlclang': [],
+ 'ibm-xlc': [],
+}
+
+_ld_args = {
+ 'ibm-openxl': [],
+ 'ibm-xlclang': ['-Wl,dll', '-q64'],
+ 'ibm-xlc': ['-Wl,dll', '-q64'],
+}
+
+
+# Python on z/OS is built with no compiler specific options in it's CFLAGS.
+# But each compiler requires it's own specific options to build successfully,
+# though some of the options are common between them
+class Compiler(unix.Compiler):
+ src_extensions = ['.c', '.C', '.cc', '.cxx', '.cpp', '.m', '.s']
+ _cpp_extensions = ['.cc', '.cpp', '.cxx', '.C']
+ _asm_extensions = ['.s']
+
+ def _get_zos_compiler_name(self):
+ zos_compiler_names = [
+ os.path.basename(binary)
+ for envvar in ('CC', 'CXX', 'LDSHARED')
+ if (binary := os.environ.get(envvar, None))
+ ]
+ if len(zos_compiler_names) == 0:
+ return 'ibm-openxl'
+
+ zos_compilers = {}
+ for compiler in (
+ 'ibm-clang',
+ 'ibm-clang64',
+ 'ibm-clang++',
+ 'ibm-clang++64',
+ 'clang',
+ 'clang++',
+ 'clang-14',
+ ):
+ zos_compilers[compiler] = 'ibm-openxl'
+
+ for compiler in ('xlclang', 'xlclang++', 'njsc', 'njsc++'):
+ zos_compilers[compiler] = 'ibm-xlclang'
+
+ for compiler in ('xlc', 'xlC', 'xlc++'):
+ zos_compilers[compiler] = 'ibm-xlc'
+
+ return zos_compilers.get(zos_compiler_names[0], 'ibm-openxl')
+
+ def __init__(self, verbose=False, dry_run=False, force=False):
+ super().__init__(verbose, dry_run, force)
+ self.zos_compiler = self._get_zos_compiler_name()
+ sysconfig.customize_compiler(self)
+
+ def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+ local_args = []
+ if ext in self._cpp_extensions:
+ compiler = self.compiler_cxx
+ local_args.extend(_cxx_args[self.zos_compiler])
+ elif ext in self._asm_extensions:
+ compiler = self.compiler_so
+ local_args.extend(_cc_args[self.zos_compiler])
+ local_args.extend(_asm_args[self.zos_compiler])
+ else:
+ compiler = self.compiler_so
+ local_args.extend(_cc_args[self.zos_compiler])
+ local_args.extend(cc_args)
+
+ try:
+ self.spawn(compiler + local_args + [src, '-o', obj] + extra_postargs)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
+
+ def runtime_library_dir_option(self, dir):
+ return '-L' + dir
+
+ def link(
+ self,
+ target_desc,
+ objects,
+ output_filename,
+ output_dir=None,
+ libraries=None,
+ library_dirs=None,
+ runtime_library_dirs=None,
+ export_symbols=None,
+ debug=False,
+ extra_preargs=None,
+ extra_postargs=None,
+ build_temp=None,
+ target_lang=None,
+ ):
+ # For a built module to use functions from cpython, it needs to use Pythons
+ # side deck file. The side deck is located beside the libpython3.xx.so
+ ldversion = sysconfig.get_config_var('LDVERSION')
+ if sysconfig.python_build:
+ side_deck_path = os.path.join(
+ sysconfig.get_config_var('abs_builddir'),
+ f'libpython{ldversion}.x',
+ )
+ else:
+ side_deck_path = os.path.join(
+ sysconfig.get_config_var('installed_base'),
+ sysconfig.get_config_var('platlibdir'),
+ f'libpython{ldversion}.x',
+ )
+
+ if os.path.exists(side_deck_path):
+ if extra_postargs:
+ extra_postargs.append(side_deck_path)
+ else:
+ extra_postargs = [side_deck_path]
+
+ # Check and replace libraries included side deck files
+ if runtime_library_dirs:
+ for dir in runtime_library_dirs:
+ for library in libraries[:]:
+ library_side_deck = os.path.join(dir, f'{library}.x')
+ if os.path.exists(library_side_deck):
+ libraries.remove(library)
+ extra_postargs.append(library_side_deck)
+ break
+
+ # Any required ld args for the given compiler
+ extra_postargs.extend(_ld_args[self.zos_compiler])
+
+ super().link(
+ target_desc,
+ objects,
+ output_filename,
+ output_dir,
+ libraries,
+ library_dirs,
+ runtime_library_dirs,
+ export_symbols,
+ debug,
+ extra_preargs,
+ extra_postargs,
+ build_temp,
+ target_lang,
+ )
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compilers/__init__.py b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/__init__.py
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compilers/__init__.py
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py
index 3c67524e6d9..de89e3cd840 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py
@@ -1,335 +1,27 @@
-"""distutils.cygwinccompiler
-
-Provides the CygwinCCompiler class, a subclass of UnixCCompiler that
-handles the Cygwin port of the GNU C compiler to Windows. It also contains
-the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
-cygwin in no-cygwin mode).
-"""
-
-import copy
-import os
-import pathlib
-import shlex
-import sys
-import warnings
-from subprocess import check_output
-
-from .errors import (
- CCompilerError,
- CompileError,
- DistutilsExecError,
- DistutilsPlatformError,
+from .compilers.C import cygwin
+from .compilers.C.cygwin import (
+ CONFIG_H_NOTOK,
+ CONFIG_H_OK,
+ CONFIG_H_UNCERTAIN,
+ check_config_h,
+ get_msvcr,
+ is_cygwincc,
)
-from .file_util import write_file
-from .sysconfig import get_config_vars
-from .unixccompiler import UnixCCompiler
-from .version import LooseVersion, suppress_known_deprecation
-
-
-def get_msvcr():
- """No longer needed, but kept for backward compatibility."""
- return []
-
-
-_runtime_library_dirs_msg = (
- "Unable to set runtime library search path on Windows, "
- "usually indicated by `runtime_library_dirs` parameter to Extension"
-)
-
-
-class CygwinCCompiler(UnixCCompiler):
- """Handles the Cygwin port of the GNU C compiler to Windows."""
-
- compiler_type = 'cygwin'
- obj_extension = ".o"
- static_lib_extension = ".a"
- shared_lib_extension = ".dll.a"
- dylib_lib_extension = ".dll"
- static_lib_format = "lib%s%s"
- shared_lib_format = "lib%s%s"
- dylib_lib_format = "cyg%s%s"
- exe_extension = ".exe"
-
- def __init__(self, verbose=False, dry_run=False, force=False):
- super().__init__(verbose, dry_run, force)
-
- status, details = check_config_h()
- self.debug_print(f"Python's GCC status: {status} (details: {details})")
- if status is not CONFIG_H_OK:
- self.warn(
- "Python's pyconfig.h doesn't seem to support your compiler. "
- f"Reason: {details}. "
- "Compiling may fail because of undefined preprocessor macros."
- )
-
- self.cc, self.cxx = get_config_vars('CC', 'CXX')
-
- # Override 'CC' and 'CXX' environment variables for
- # building using MINGW compiler for MSVC python.
- self.cc = os.environ.get('CC', self.cc or 'gcc')
- self.cxx = os.environ.get('CXX', self.cxx or 'g++')
-
- self.linker_dll = self.cc
- self.linker_dll_cxx = self.cxx
- shared_option = "-shared"
-
- self.set_executables(
- compiler=f'{self.cc} -mcygwin -O -Wall',
- compiler_so=f'{self.cc} -mcygwin -mdll -O -Wall',
- compiler_cxx=f'{self.cxx} -mcygwin -O -Wall',
- compiler_so_cxx=f'{self.cxx} -mcygwin -mdll -O -Wall',
- linker_exe=f'{self.cc} -mcygwin',
- linker_so=f'{self.linker_dll} -mcygwin {shared_option}',
- linker_exe_cxx=f'{self.cxx} -mcygwin',
- linker_so_cxx=f'{self.linker_dll_cxx} -mcygwin {shared_option}',
- )
-
- self.dll_libraries = get_msvcr()
-
- @property
- def gcc_version(self):
- # Older numpy depended on this existing to check for ancient
- # gcc versions. This doesn't make much sense with clang etc so
- # just hardcode to something recent.
- # https://github.com/numpy/numpy/pull/20333
- warnings.warn(
- "gcc_version attribute of CygwinCCompiler is deprecated. "
- "Instead of returning actual gcc version a fixed value 11.2.0 is returned.",
- DeprecationWarning,
- stacklevel=2,
- )
- with suppress_known_deprecation():
- return LooseVersion("11.2.0")
-
- def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
- """Compiles the source by spawning GCC and windres if needed."""
- if ext in ('.rc', '.res'):
- # gcc needs '.res' and '.rc' compiled to object files !!!
- try:
- self.spawn(["windres", "-i", src, "-o", obj])
- except DistutilsExecError as msg:
- raise CompileError(msg)
- else: # for other files use the C-compiler
- try:
- if self.detect_language(src) == 'c++':
- self.spawn(
- self.compiler_so_cxx
- + cc_args
- + [src, '-o', obj]
- + extra_postargs
- )
- else:
- self.spawn(
- self.compiler_so + cc_args + [src, '-o', obj] + extra_postargs
- )
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- def link(
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- """Link the objects."""
- # use separate copies, so we can modify the lists
- extra_preargs = copy.copy(extra_preargs or [])
- libraries = copy.copy(libraries or [])
- objects = copy.copy(objects or [])
-
- if runtime_library_dirs:
- self.warn(_runtime_library_dirs_msg)
-
- # Additional libraries
- libraries.extend(self.dll_libraries)
-
- # handle export symbols by creating a def-file
- # with executables this only works with gcc/ld as linker
- if (export_symbols is not None) and (
- target_desc != self.EXECUTABLE or self.linker_dll == "gcc"
- ):
- # (The linker doesn't do anything if output is up-to-date.
- # So it would probably better to check if we really need this,
- # but for this we had to insert some unchanged parts of
- # UnixCCompiler, and this is not what we want.)
-
- # we want to put some files in the same directory as the
- # object files are, build_temp doesn't help much
- # where are the object files
- temp_dir = os.path.dirname(objects[0])
- # name of dll to give the helper files the same base name
- (dll_name, dll_extension) = os.path.splitext(
- os.path.basename(output_filename)
- )
-
- # generate the filenames for these files
- def_file = os.path.join(temp_dir, dll_name + ".def")
-
- # Generate .def file
- contents = [f"LIBRARY {os.path.basename(output_filename)}", "EXPORTS"]
- contents.extend(export_symbols)
- self.execute(write_file, (def_file, contents), f"writing {def_file}")
-
- # next add options for def-file
-
- # for gcc/ld the def-file is specified as any object files
- objects.append(def_file)
-
- # end: if ((export_symbols is not None) and
- # (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
-
- # who wants symbols and a many times larger output file
- # should explicitly switch the debug mode on
- # otherwise we let ld strip the output file
- # (On my machine: 10KiB < stripped_file < ??100KiB
- # unstripped_file = stripped_file + XXX KiB
- # ( XXX=254 for a typical python extension))
- if not debug:
- extra_preargs.append("-s")
-
- UnixCCompiler.link(
- self,
- target_desc,
- objects,
- output_filename,
- output_dir,
- libraries,
- library_dirs,
- runtime_library_dirs,
- None, # export_symbols, we do this in our def-file
- debug,
- extra_preargs,
- extra_postargs,
- build_temp,
- target_lang,
- )
-
- def runtime_library_dir_option(self, dir):
- # cygwin doesn't support rpath. While in theory we could error
- # out like MSVC does, code might expect it to work like on Unix, so
- # just warn and hope for the best.
- self.warn(_runtime_library_dirs_msg)
- return []
-
- # -- Miscellaneous methods -----------------------------------------
-
- def _make_out_path(self, output_dir, strip_dir, src_name):
- # use normcase to make sure '.rc' is really '.rc' and not '.RC'
- norm_src_name = os.path.normcase(src_name)
- return super()._make_out_path(output_dir, strip_dir, norm_src_name)
-
- @property
- def out_extensions(self):
- """
- Add support for rc and res files.
- """
- return {
- **super().out_extensions,
- **{ext: ext + self.obj_extension for ext in ('.res', '.rc')},
- }
-
-
-# the same as cygwin plus some additional parameters
-class Mingw32CCompiler(CygwinCCompiler):
- """Handles the Mingw32 port of the GNU C compiler to Windows."""
-
- compiler_type = 'mingw32'
-
- def __init__(self, verbose=False, dry_run=False, force=False):
- super().__init__(verbose, dry_run, force)
-
- shared_option = "-shared"
-
- if is_cygwincc(self.cc):
- raise CCompilerError('Cygwin gcc cannot be used with --compiler=mingw32')
-
- self.set_executables(
- compiler=f'{self.cc} -O -Wall',
- compiler_so=f'{self.cc} -shared -O -Wall',
- compiler_so_cxx=f'{self.cxx} -shared -O -Wall',
- compiler_cxx=f'{self.cxx} -O -Wall',
- linker_exe=f'{self.cc}',
- linker_so=f'{self.linker_dll} {shared_option}',
- linker_exe_cxx=f'{self.cxx}',
- linker_so_cxx=f'{self.linker_dll_cxx} {shared_option}',
- )
-
- def runtime_library_dir_option(self, dir):
- raise DistutilsPlatformError(_runtime_library_dirs_msg)
-
-
-# Because these compilers aren't configured in Python's pyconfig.h file by
-# default, we should at least warn the user if he is using an unmodified
-# version.
-
-CONFIG_H_OK = "ok"
-CONFIG_H_NOTOK = "not ok"
-CONFIG_H_UNCERTAIN = "uncertain"
-
-
-def check_config_h():
- """Check if the current Python installation appears amenable to building
- extensions with GCC.
-
- Returns a tuple (status, details), where 'status' is one of the following
- constants:
-
- - CONFIG_H_OK: all is well, go ahead and compile
- - CONFIG_H_NOTOK: doesn't look good
- - CONFIG_H_UNCERTAIN: not sure -- unable to read pyconfig.h
-
- 'details' is a human-readable string explaining the situation.
-
- Note there are two ways to conclude "OK": either 'sys.version' contains
- the string "GCC" (implying that this Python was built with GCC), or the
- installed "pyconfig.h" contains the string "__GNUC__".
- """
-
- # XXX since this function also checks sys.version, it's not strictly a
- # "pyconfig.h" check -- should probably be renamed...
-
- from distutils import sysconfig
-
- # if sys.version contains GCC then python was compiled with GCC, and the
- # pyconfig.h file should be OK
- if "GCC" in sys.version:
- return CONFIG_H_OK, "sys.version mentions 'GCC'"
-
- # Clang would also work
- if "Clang" in sys.version:
- return CONFIG_H_OK, "sys.version mentions 'Clang'"
- # let's see if __GNUC__ is mentioned in python.h
- fn = sysconfig.get_config_h_filename()
- try:
- config_h = pathlib.Path(fn).read_text(encoding='utf-8')
- except OSError as exc:
- return (CONFIG_H_UNCERTAIN, f"couldn't read '{fn}': {exc.strerror}")
- else:
- substring = '__GNUC__'
- if substring in config_h:
- code = CONFIG_H_OK
- mention_inflected = 'mentions'
- else:
- code = CONFIG_H_NOTOK
- mention_inflected = 'does not mention'
- return code, f"{fn!r} {mention_inflected} {substring!r}"
+__all__ = [
+ 'CONFIG_H_NOTOK',
+ 'CONFIG_H_OK',
+ 'CONFIG_H_UNCERTAIN',
+ 'CygwinCCompiler',
+ 'Mingw32CCompiler',
+ 'check_config_h',
+ 'get_msvcr',
+ 'is_cygwincc',
+]
-def is_cygwincc(cc):
- """Try to determine if the compiler that would be used is from cygwin."""
- out_string = check_output(shlex.split(cc) + ['-dumpmachine'])
- return out_string.strip().endswith(b'cygwin')
+CygwinCCompiler = cygwin.Compiler
+Mingw32CCompiler = cygwin.MinGW32Compiler
get_versions = None
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/dist.py b/contrib/python/setuptools/py3/setuptools/_distutils/dist.py
index 33ed8ebd7a0..37b788df925 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/dist.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/dist.py
@@ -13,9 +13,18 @@ import pathlib
import re
import sys
import warnings
-from collections.abc import Iterable
+from collections.abc import Iterable, MutableMapping
from email import message_from_file
-from typing import TYPE_CHECKING, Literal, TypeVar, overload
+from typing import (
+ IO,
+ TYPE_CHECKING,
+ Any,
+ ClassVar,
+ Literal,
+ TypeVar,
+ Union,
+ overload,
+)
from packaging.utils import canonicalize_name, canonicalize_version
@@ -31,10 +40,17 @@ from .fancy_getopt import FancyGetopt, translate_longopt
from .util import check_environ, rfc822_escape, strtobool
if TYPE_CHECKING:
+ from _typeshed import SupportsWrite
+ from typing_extensions import TypeAlias
+
# type-only import because of mutual dependence between these modules
from .cmd import Command
_CommandT = TypeVar("_CommandT", bound="Command")
+_OptionsList: TypeAlias = list[
+ Union[tuple[str, Union[str, None], str, int], tuple[str, Union[str, None], str]]
+]
+
# Regex to define acceptable Distutils command names. This is not *quite*
# the same as a Python NAME -- I don't allow leading underscores. The fact
@@ -43,7 +59,7 @@ _CommandT = TypeVar("_CommandT", bound="Command")
command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$')
-def _ensure_list(value, fieldname):
+def _ensure_list(value: str | Iterable[str], fieldname) -> str | list[str]:
if isinstance(value, str):
# a string containing comma separated values is okay. It will
# be converted to a list by Distribution.finalize_options().
@@ -80,7 +96,7 @@ class Distribution:
# don't want to pollute the commands with too many options that they
# have minimal control over.
# The fourth entry for verbose means that it can be repeated.
- global_options = [
+ global_options: ClassVar[_OptionsList] = [
('verbose', 'v', "run verbosely (default)", 1),
('quiet', 'q', "run quietly (turns verbosity off)"),
('dry-run', 'n', "don't actually do anything"),
@@ -90,7 +106,7 @@ class Distribution:
# 'common_usage' is a short (2-3 line) string describing the common
# usage of the setup script.
- common_usage = """\
+ common_usage: ClassVar[str] = """\
Common commands: (see '--help-commands' for more)
setup.py build will build the package underneath 'build/'
@@ -98,7 +114,7 @@ Common commands: (see '--help-commands' for more)
"""
# options that are not propagated to the commands
- display_options = [
+ display_options: ClassVar[_OptionsList] = [
('help-commands', None, "list all available commands"),
('name', None, "print package name"),
('version', 'V', "print package version"),
@@ -125,14 +141,17 @@ Common commands: (see '--help-commands' for more)
('requires', None, "print the list of packages/modules required"),
('obsoletes', None, "print the list of packages/modules made obsolete"),
]
- display_option_names = [translate_longopt(x[0]) for x in display_options]
+ display_option_names: ClassVar[list[str]] = [
+ translate_longopt(x[0]) for x in display_options
+ ]
# negative options are options that exclude other options
- negative_opt = {'quiet': 'verbose'}
+ negative_opt: ClassVar[dict[str, str]] = {'quiet': 'verbose'}
# -- Creation/initialization methods -------------------------------
- def __init__(self, attrs=None): # noqa: C901
+ # Can't Unpack a TypedDict with optional properties, so using Any instead
+ def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None: # noqa: C901
"""Construct a new Distribution instance: initialize all the
attributes of a Distribution, and then use 'attrs' (a dictionary
mapping attribute names to values) to assign some of those
@@ -164,7 +183,7 @@ Common commands: (see '--help-commands' for more)
# can 1) quickly figure out which class to instantiate when
# we need to create a new command object, and 2) have a way
# for the setup script to override command classes
- self.cmdclass = {}
+ self.cmdclass: dict[str, type[Command]] = {}
# 'command_packages' is a list of packages in which commands
# are searched for. The factory for command 'foo' is expected
@@ -172,12 +191,12 @@ Common commands: (see '--help-commands' for more)
# named here. This list is searched from the left; an error
# is raised if no named package provides the command being
# searched for. (Always access using get_command_packages().)
- self.command_packages = None
+ self.command_packages: str | list[str] | None = None
# 'script_name' and 'script_args' are usually set to sys.argv[0]
# and sys.argv[1:], but they can be overridden when the caller is
# not necessarily a setup script run from the command-line.
- self.script_name = None
+ self.script_name: str | os.PathLike[str] | None = None
self.script_args: list[str] | None = None
# 'command_options' is where we store command options between
@@ -185,7 +204,7 @@ Common commands: (see '--help-commands' for more)
# they are actually needed -- ie. when the command in question is
# instantiated. It is a dictionary of dictionaries of 2-tuples:
# command_options = { command_name : { option : (source, value) } }
- self.command_options = {}
+ self.command_options: dict[str, dict[str, tuple[str, str]]] = {}
# 'dist_files' is the list of (command, pyversion, file) that
# have been created by any dist commands run so far. This is
@@ -196,13 +215,13 @@ Common commands: (see '--help-commands' for more)
# file. pyversion should not be used to specify minimum or
# maximum required Python versions; use the metainfo for that
# instead.
- self.dist_files = []
+ self.dist_files: list[tuple[str, str, str]] = []
# These options are really the business of various commands, rather
# than of the Distribution itself. We provide aliases for them in
# Distribution as a convenience to the developer.
self.packages = None
- self.package_data = {}
+ self.package_data: dict[str, list[str]] = {}
self.package_dir = None
self.py_modules = None
self.libraries = None
@@ -219,7 +238,7 @@ Common commands: (see '--help-commands' for more)
# the caller at all. 'command_obj' maps command names to
# Command instances -- that's how we enforce that every command
# class is a singleton.
- self.command_obj = {}
+ self.command_obj: dict[str, Command] = {}
# 'have_run' maps command names to boolean values; it keeps track
# of whether we have actually run a particular command, to make it
@@ -231,7 +250,7 @@ Common commands: (see '--help-commands' for more)
# command object is created, and replaced with a true value when
# the command is successfully run. Thus it's probably best to use
# '.get()' rather than a straight lookup.
- self.have_run = {}
+ self.have_run: dict[str, bool] = {}
# Now we'll use the attrs dictionary (ultimately, keyword args from
# the setup script) to possibly override any or all of these
@@ -300,7 +319,7 @@ Common commands: (see '--help-commands' for more)
dict = self.command_options[command] = {}
return dict
- def dump_option_dicts(self, header=None, commands=None, indent=""):
+ def dump_option_dicts(self, header=None, commands=None, indent: str = "") -> None:
from pprint import pformat
if commands is None: # dump all command option dicts
@@ -615,7 +634,7 @@ Common commands: (see '--help-commands' for more)
return args
- def finalize_options(self):
+ def finalize_options(self) -> None:
"""Set final values for all the options on the Distribution
instance, analogous to the .finalize_options() method of Command
objects.
@@ -718,7 +737,7 @@ Common commands: (see '--help-commands' for more)
return any_display_options
- def print_command_list(self, commands, header, max_length):
+ def print_command_list(self, commands, header, max_length) -> None:
"""Print a subset of the list of all commands -- used by
'print_commands()'.
"""
@@ -735,7 +754,7 @@ Common commands: (see '--help-commands' for more)
print(f" {cmd:<{max_length}} {description}")
- def print_commands(self):
+ def print_commands(self) -> None:
"""Print out a help message listing all available commands with a
description of each. The list is divided into "standard commands"
(listed in distutils.command.__all__) and "extra commands"
@@ -802,7 +821,7 @@ Common commands: (see '--help-commands' for more)
self.command_packages = pkgs
return pkgs
- def get_command_class(self, command):
+ def get_command_class(self, command: str) -> type[Command]:
"""Return the class that implements the Distutils command named by
'command'. First we check the 'cmdclass' dictionary; if the
command is mentioned there, we fetch the class object from the
@@ -971,10 +990,10 @@ Common commands: (see '--help-commands' for more)
# -- Methods that operate on the Distribution ----------------------
- def announce(self, msg, level=logging.INFO):
+ def announce(self, msg, level: int = logging.INFO) -> None:
log.log(level, msg)
- def run_commands(self):
+ def run_commands(self) -> None:
"""Run each command that was seen on the setup script command line.
Uses the list of commands found and cache of command objects
created by 'get_command_obj()'.
@@ -984,7 +1003,7 @@ Common commands: (see '--help-commands' for more)
# -- Methods that operate on its Commands --------------------------
- def run_command(self, command):
+ def run_command(self, command: str) -> None:
"""Do whatever it takes to run a command (including nothing at all,
if the command has already been run). Specifically: if we have
already created and run the command named by 'command', return
@@ -1004,28 +1023,28 @@ Common commands: (see '--help-commands' for more)
# -- Distribution query methods ------------------------------------
- def has_pure_modules(self):
+ def has_pure_modules(self) -> bool:
return len(self.packages or self.py_modules or []) > 0
- def has_ext_modules(self):
+ def has_ext_modules(self) -> bool:
return self.ext_modules and len(self.ext_modules) > 0
- def has_c_libraries(self):
+ def has_c_libraries(self) -> bool:
return self.libraries and len(self.libraries) > 0
- def has_modules(self):
+ def has_modules(self) -> bool:
return self.has_pure_modules() or self.has_ext_modules()
- def has_headers(self):
+ def has_headers(self) -> bool:
return self.headers and len(self.headers) > 0
- def has_scripts(self):
+ def has_scripts(self) -> bool:
return self.scripts and len(self.scripts) > 0
- def has_data_files(self):
+ def has_data_files(self) -> bool:
return self.data_files and len(self.data_files) > 0
- def is_pure(self):
+ def is_pure(self) -> bool:
return (
self.has_pure_modules()
and not self.has_ext_modules()
@@ -1038,6 +1057,53 @@ Common commands: (see '--help-commands' for more)
# they are defined in a sneaky way: the constructor binds self.get_XXX
# to self.metadata.get_XXX. The actual code is in the
# DistributionMetadata class, below.
+ if TYPE_CHECKING:
+ # Unfortunately this means we need to specify them manually or not expose statically
+ def _(self) -> None:
+ self.get_name = self.metadata.get_name
+ self.get_version = self.metadata.get_version
+ self.get_fullname = self.metadata.get_fullname
+ self.get_author = self.metadata.get_author
+ self.get_author_email = self.metadata.get_author_email
+ self.get_maintainer = self.metadata.get_maintainer
+ self.get_maintainer_email = self.metadata.get_maintainer_email
+ self.get_contact = self.metadata.get_contact
+ self.get_contact_email = self.metadata.get_contact_email
+ self.get_url = self.metadata.get_url
+ self.get_license = self.metadata.get_license
+ self.get_licence = self.metadata.get_licence
+ self.get_description = self.metadata.get_description
+ self.get_long_description = self.metadata.get_long_description
+ self.get_keywords = self.metadata.get_keywords
+ self.get_platforms = self.metadata.get_platforms
+ self.get_classifiers = self.metadata.get_classifiers
+ self.get_download_url = self.metadata.get_download_url
+ self.get_requires = self.metadata.get_requires
+ self.get_provides = self.metadata.get_provides
+ self.get_obsoletes = self.metadata.get_obsoletes
+
+ # Default attributes generated in __init__ from self.display_option_names
+ help_commands: bool
+ name: str | Literal[False]
+ version: str | Literal[False]
+ fullname: str | Literal[False]
+ author: str | Literal[False]
+ author_email: str | Literal[False]
+ maintainer: str | Literal[False]
+ maintainer_email: str | Literal[False]
+ contact: str | Literal[False]
+ contact_email: str | Literal[False]
+ url: str | Literal[False]
+ license: str | Literal[False]
+ licence: str | Literal[False]
+ description: str | Literal[False]
+ long_description: str | Literal[False]
+ platforms: str | list[str] | Literal[False]
+ classifiers: str | list[str] | Literal[False]
+ keywords: str | list[str] | Literal[False]
+ provides: list[str] | Literal[False]
+ requires: list[str] | Literal[False]
+ obsoletes: list[str] | Literal[False]
class DistributionMetadata:
@@ -1069,37 +1135,40 @@ class DistributionMetadata:
"obsoletes",
)
- def __init__(self, path=None):
+ def __init__(
+ self, path: str | bytes | os.PathLike[str] | os.PathLike[bytes] | None = None
+ ) -> None:
if path is not None:
self.read_pkg_file(open(path))
else:
- self.name = None
- self.version = None
- self.author = None
- self.author_email = None
- self.maintainer = None
- self.maintainer_email = None
- self.url = None
- self.license = None
- self.description = None
- self.long_description = None
- self.keywords = None
- self.platforms = None
- self.classifiers = None
- self.download_url = None
+ self.name: str | None = None
+ self.version: str | None = None
+ self.author: str | None = None
+ self.author_email: str | None = None
+ self.maintainer: str | None = None
+ self.maintainer_email: str | None = None
+ self.url: str | None = None
+ self.license: str | None = None
+ self.description: str | None = None
+ self.long_description: str | None = None
+ self.keywords: str | list[str] | None = None
+ self.platforms: str | list[str] | None = None
+ self.classifiers: str | list[str] | None = None
+ self.download_url: str | None = None
# PEP 314
- self.provides = None
- self.requires = None
- self.obsoletes = None
+ self.provides: str | list[str] | None = None
+ self.requires: str | list[str] | None = None
+ self.obsoletes: str | list[str] | None = None
- def read_pkg_file(self, file):
+ def read_pkg_file(self, file: IO[str]) -> None:
"""Reads the metadata values from a file object."""
msg = message_from_file(file)
- def _read_field(name):
+ def _read_field(name: str) -> str | None:
value = msg[name]
if value and value != "UNKNOWN":
return value
+ return None
def _read_list(name):
values = msg.get_all(name, None)
@@ -1143,14 +1212,14 @@ class DistributionMetadata:
self.provides = None
self.obsoletes = None
- def write_pkg_info(self, base_dir):
+ def write_pkg_info(self, base_dir: str | os.PathLike[str]) -> None:
"""Write the PKG-INFO file into the release tree."""
with open(
os.path.join(base_dir, 'PKG-INFO'), 'w', encoding='UTF-8'
) as pkg_info:
self.write_pkg_file(pkg_info)
- def write_pkg_file(self, file):
+ def write_pkg_file(self, file: SupportsWrite[str]) -> None:
"""Write the PKG-INFO format data to a file object."""
version = '1.0'
if (
@@ -1196,13 +1265,13 @@ class DistributionMetadata:
# -- Metadata query methods ----------------------------------------
- def get_name(self):
+ def get_name(self) -> str:
return self.name or "UNKNOWN"
- def get_version(self):
+ def get_version(self) -> str:
return self.version or "0.0.0"
- def get_fullname(self):
+ def get_fullname(self) -> str:
return self._fullname(self.get_name(), self.get_version())
@staticmethod
@@ -1224,74 +1293,74 @@ class DistributionMetadata:
canonicalize_version(version, strip_trailing_zero=False),
)
- def get_author(self):
+ def get_author(self) -> str | None:
return self.author
- def get_author_email(self):
+ def get_author_email(self) -> str | None:
return self.author_email
- def get_maintainer(self):
+ def get_maintainer(self) -> str | None:
return self.maintainer
- def get_maintainer_email(self):
+ def get_maintainer_email(self) -> str | None:
return self.maintainer_email
- def get_contact(self):
+ def get_contact(self) -> str | None:
return self.maintainer or self.author
- def get_contact_email(self):
+ def get_contact_email(self) -> str | None:
return self.maintainer_email or self.author_email
- def get_url(self):
+ def get_url(self) -> str | None:
return self.url
- def get_license(self):
+ def get_license(self) -> str | None:
return self.license
get_licence = get_license
- def get_description(self):
+ def get_description(self) -> str | None:
return self.description
- def get_long_description(self):
+ def get_long_description(self) -> str | None:
return self.long_description
- def get_keywords(self):
+ def get_keywords(self) -> str | list[str]:
return self.keywords or []
- def set_keywords(self, value):
+ def set_keywords(self, value: str | Iterable[str]) -> None:
self.keywords = _ensure_list(value, 'keywords')
- def get_platforms(self):
+ def get_platforms(self) -> str | list[str] | None:
return self.platforms
- def set_platforms(self, value):
+ def set_platforms(self, value: str | Iterable[str]) -> None:
self.platforms = _ensure_list(value, 'platforms')
- def get_classifiers(self):
+ def get_classifiers(self) -> str | list[str]:
return self.classifiers or []
- def set_classifiers(self, value):
+ def set_classifiers(self, value: str | Iterable[str]) -> None:
self.classifiers = _ensure_list(value, 'classifiers')
- def get_download_url(self):
+ def get_download_url(self) -> str | None:
return self.download_url
# PEP 314
- def get_requires(self):
+ def get_requires(self) -> str | list[str]:
return self.requires or []
- def set_requires(self, value):
+ def set_requires(self, value: Iterable[str]) -> None:
import distutils.versionpredicate
for v in value:
distutils.versionpredicate.VersionPredicate(v)
self.requires = list(value)
- def get_provides(self):
+ def get_provides(self) -> str | list[str]:
return self.provides or []
- def set_provides(self, value):
+ def set_provides(self, value: Iterable[str]) -> None:
value = [v.strip() for v in value]
for v in value:
import distutils.versionpredicate
@@ -1299,10 +1368,10 @@ class DistributionMetadata:
distutils.versionpredicate.split_provision(v)
self.provides = value
- def get_obsoletes(self):
+ def get_obsoletes(self) -> str | list[str]:
return self.obsoletes or []
- def set_obsoletes(self, value):
+ def set_obsoletes(self, value: Iterable[str]) -> None:
import distutils.versionpredicate
for v in value:
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/errors.py b/contrib/python/setuptools/py3/setuptools/_distutils/errors.py
index 3196a4f0972..409d21faa2c 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/errors.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/errors.py
@@ -5,6 +5,17 @@ Distutils modules may raise these or standard exceptions,
including :exc:`SystemExit`.
"""
+# compiler exceptions aliased for compatibility
+from .compilers.C.errors import CompileError as CompileError
+from .compilers.C.errors import Error as _Error
+from .compilers.C.errors import LibError as LibError
+from .compilers.C.errors import LinkError as LinkError
+from .compilers.C.errors import PreprocessError as PreprocessError
+from .compilers.C.errors import UnknownFileType as _UnknownFileType
+
+CCompilerError = _Error
+UnknownFileError = _UnknownFileType
+
class DistutilsError(Exception):
"""The root of all Distutils evil."""
@@ -95,30 +106,3 @@ class DistutilsTemplateError(DistutilsError):
class DistutilsByteCompileError(DistutilsError):
"""Byte compile error."""
-
-
-# Exception classes used by the CCompiler implementation classes
-class CCompilerError(Exception):
- """Some compile/link operation failed."""
-
-
-class PreprocessError(CCompilerError):
- """Failure to preprocess one or more C/C++ files."""
-
-
-class CompileError(CCompilerError):
- """Failure to compile one or more C/C++ source files."""
-
-
-class LibError(CCompilerError):
- """Failure to create a static library from one or more C/C++ object
- files."""
-
-
-class LinkError(CCompilerError):
- """Failure to link one or more C/C++ object files into an executable
- or shared library file."""
-
-
-class UnknownFileError(CCompilerError):
- """Attempt to process an unknown file type."""
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/extension.py b/contrib/python/setuptools/py3/setuptools/_distutils/extension.py
index e0532734362..f51411266e4 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/extension.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/extension.py
@@ -3,8 +3,11 @@
Provides the Extension class, used to describe C/C++ extension
modules in setup scripts."""
+from __future__ import annotations
+
import os
import warnings
+from collections.abc import Iterable
# This class is really only used by the "build_ext" command, so it might
# make sense to put it in distutils.command.build_ext. However, that
@@ -88,22 +91,22 @@ class Extension:
# setup_keywords in core.py.
def __init__(
self,
- name,
- sources,
- include_dirs=None,
- define_macros=None,
- undef_macros=None,
- library_dirs=None,
- libraries=None,
- runtime_library_dirs=None,
- extra_objects=None,
- extra_compile_args=None,
- extra_link_args=None,
- export_symbols=None,
- swig_opts=None,
- depends=None,
- language=None,
- optional=None,
+ name: str,
+ sources: Iterable[str | os.PathLike[str]],
+ include_dirs: list[str] | None = None,
+ define_macros: list[tuple[str, str | None]] | None = None,
+ undef_macros: list[str] | None = None,
+ library_dirs: list[str] | None = None,
+ libraries: list[str] | None = None,
+ runtime_library_dirs: list[str] | None = None,
+ extra_objects: list[str] | None = None,
+ extra_compile_args: list[str] | None = None,
+ extra_link_args: list[str] | None = None,
+ export_symbols: list[str] | None = None,
+ swig_opts: list[str] | None = None,
+ depends: list[str] | None = None,
+ language: str | None = None,
+ optional: bool | None = None,
**kw, # To catch unknown keywords
):
if not isinstance(name, str):
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/filelist.py b/contrib/python/setuptools/py3/setuptools/_distutils/filelist.py
index 9857b195493..70dc0fdebc3 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/filelist.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/filelist.py
@@ -4,10 +4,14 @@ Provides the FileList class, used for poking about the filesystem
and building lists of files.
"""
+from __future__ import annotations
+
import fnmatch
import functools
import os
import re
+from collections.abc import Iterable
+from typing import Literal, overload
from ._log import log
from .errors import DistutilsInternalError, DistutilsTemplateError
@@ -29,19 +33,19 @@ class FileList:
filtering applied)
"""
- def __init__(self, warn=None, debug_print=None):
+ def __init__(self, warn: object = None, debug_print: object = None) -> None:
# ignore argument to FileList, but keep them for backwards
# compatibility
- self.allfiles = None
- self.files = []
+ self.allfiles: Iterable[str] | None = None
+ self.files: list[str] = []
- def set_allfiles(self, allfiles):
+ def set_allfiles(self, allfiles: Iterable[str]) -> None:
self.allfiles = allfiles
- def findall(self, dir=os.curdir):
+ def findall(self, dir: str | os.PathLike[str] = os.curdir) -> None:
self.allfiles = findall(dir)
- def debug_print(self, msg):
+ def debug_print(self, msg: object) -> None:
"""Print 'msg' to stdout if the global DEBUG (taken from the
DISTUTILS_DEBUG environment variable) flag is true.
"""
@@ -52,13 +56,13 @@ class FileList:
# Collection methods
- def append(self, item):
+ def append(self, item: str) -> None:
self.files.append(item)
- def extend(self, items):
+ def extend(self, items: Iterable[str]) -> None:
self.files.extend(items)
- def sort(self):
+ def sort(self) -> None:
# Not a strict lexical sort!
sortable_files = sorted(map(os.path.split, self.files))
self.files = []
@@ -67,7 +71,7 @@ class FileList:
# Other miscellaneous utility methods
- def remove_duplicates(self):
+ def remove_duplicates(self) -> None:
# Assumes list has been sorted!
for i in range(len(self.files) - 1, 0, -1):
if self.files[i] == self.files[i - 1]:
@@ -105,7 +109,7 @@ class FileList:
return (action, patterns, dir, dir_pattern)
- def process_template_line(self, line): # noqa: C901
+ def process_template_line(self, line: str) -> None: # noqa: C901
# Parse the line: split it up, make sure the right number of words
# is there, and return the relevant words. 'action' is always
# defined: it's the first word of the line. Which of the other
@@ -193,8 +197,38 @@ class FileList:
)
# Filtering/selection methods
-
- def include_pattern(self, pattern, anchor=True, prefix=None, is_regex=False):
+ @overload
+ def include_pattern(
+ self,
+ pattern: str,
+ anchor: bool = True,
+ prefix: str | None = None,
+ is_regex: Literal[False] = False,
+ ) -> bool: ...
+ @overload
+ def include_pattern(
+ self,
+ pattern: str | re.Pattern[str],
+ anchor: bool = True,
+ prefix: str | None = None,
+ *,
+ is_regex: Literal[True],
+ ) -> bool: ...
+ @overload
+ def include_pattern(
+ self,
+ pattern: str | re.Pattern[str],
+ anchor: bool,
+ prefix: str | None,
+ is_regex: Literal[True],
+ ) -> bool: ...
+ def include_pattern(
+ self,
+ pattern: str | re.Pattern,
+ anchor: bool = True,
+ prefix: str | None = None,
+ is_regex: bool = False,
+ ) -> bool:
"""Select strings (presumably filenames) from 'self.files' that
match 'pattern', a Unix-style wildcard (glob) pattern. Patterns
are not quite the same as implemented by the 'fnmatch' module: '*'
@@ -235,7 +269,38 @@ class FileList:
files_found = True
return files_found
- def exclude_pattern(self, pattern, anchor=True, prefix=None, is_regex=False):
+ @overload
+ def exclude_pattern(
+ self,
+ pattern: str,
+ anchor: bool = True,
+ prefix: str | None = None,
+ is_regex: Literal[False] = False,
+ ) -> bool: ...
+ @overload
+ def exclude_pattern(
+ self,
+ pattern: str | re.Pattern[str],
+ anchor: bool = True,
+ prefix: str | None = None,
+ *,
+ is_regex: Literal[True],
+ ) -> bool: ...
+ @overload
+ def exclude_pattern(
+ self,
+ pattern: str | re.Pattern[str],
+ anchor: bool,
+ prefix: str | None,
+ is_regex: Literal[True],
+ ) -> bool: ...
+ def exclude_pattern(
+ self,
+ pattern: str | re.Pattern,
+ anchor: bool = True,
+ prefix: str | None = None,
+ is_regex: bool = False,
+ ) -> bool:
"""Remove strings (presumably filenames) from 'files' that match
'pattern'. Other parameters are the same as for
'include_pattern()', above.
@@ -294,7 +359,7 @@ class _UniqueDirs(set):
return filter(cls(), items)
-def findall(dir=os.curdir):
+def findall(dir: str | os.PathLike[str] = os.curdir):
"""
Find all files under 'dir' and return the list of full filenames.
Unless dir is '.', return full filenames with dir prepended.
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py b/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py
index ba280334d19..973668f2684 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/spawn.py
@@ -12,12 +12,19 @@ import shutil
import subprocess
import sys
import warnings
-from collections.abc import Mapping
+from collections.abc import Mapping, MutableSequence
+from typing import TYPE_CHECKING, TypeVar, overload
from ._log import log
from .debug import DEBUG
from .errors import DistutilsExecError
+if TYPE_CHECKING:
+ from subprocess import _ENV
+
+
+_MappingT = TypeVar("_MappingT", bound=Mapping)
+
def _debug(cmd):
"""
@@ -26,7 +33,7 @@ def _debug(cmd):
return cmd if DEBUG else cmd[0]
-def _inject_macos_ver(env: Mapping[str:str] | None) -> Mapping[str:str] | None:
+def _inject_macos_ver(env: _MappingT | None) -> _MappingT | dict[str, str | int] | None:
if platform.system() != 'Darwin':
return env
@@ -37,11 +44,21 @@ def _inject_macos_ver(env: Mapping[str:str] | None) -> Mapping[str:str] | None:
return {**_resolve(env), **update}
-def _resolve(env: Mapping[str:str] | None) -> Mapping[str:str]:
+@overload
+def _resolve(env: None) -> os._Environ[str]: ...
+@overload
+def _resolve(env: _MappingT) -> _MappingT: ...
+def _resolve(env: _MappingT | None) -> _MappingT | os._Environ[str]:
return os.environ if env is None else env
-def spawn(cmd, search_path=True, verbose=False, dry_run=False, env=None):
+def spawn(
+ cmd: MutableSequence[bytes | str | os.PathLike[str]],
+ search_path: bool = True,
+ verbose: bool = False,
+ dry_run: bool = False,
+ env: _ENV | None = None,
+) -> None:
"""Run another program, specified as a command list 'cmd', in a new process.
'cmd' is just the argument list for the new process, ie.
@@ -78,7 +95,7 @@ def spawn(cmd, search_path=True, verbose=False, dry_run=False, env=None):
) from err
-def find_executable(executable, path=None):
+def find_executable(executable: str, path: str | None = None) -> str | None:
"""Tries to find 'executable' in the directories listed in 'path'.
A string listing directories separated by 'os.pathsep'; defaults to
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py b/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py
index ef3def83eb6..7ddc869ab56 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py
@@ -9,19 +9,31 @@ Written by: Fred L. Drake, Jr.
"""
+from __future__ import annotations
+
import functools
import os
import pathlib
import re
import sys
import sysconfig
+from typing import TYPE_CHECKING, Literal, overload
from jaraco.functools import pass_none
+from .ccompiler import CCompiler
from .compat import py39
from .errors import DistutilsPlatformError
from .util import is_mingw
+if TYPE_CHECKING:
+ from typing_extensions import deprecated
+else:
+
+ def deprecated(message):
+ return lambda fn: fn
+
+
IS_PYPY = '__pypy__' in sys.builtin_module_names
# These are needed in a couple of spots, so just compute them once.
@@ -110,7 +122,7 @@ def get_python_version():
return f'{sys.version_info.major}.{sys.version_info.minor}'
-def get_python_inc(plat_specific=False, prefix=None):
+def get_python_inc(plat_specific: bool = False, prefix: str | None = None) -> str:
"""Return the directory containing installed Python header files.
If 'plat_specific' is false (the default), this is the path to the
@@ -144,8 +156,6 @@ def _extant(path):
def _get_python_inc_posix(prefix, spec_prefix, plat_specific):
- if IS_PYPY and sys.version_info < (3, 8):
- return os.path.join(prefix, 'include')
return (
_get_python_inc_posix_python(plat_specific)
or _extant(_get_python_inc_from_config(plat_specific, spec_prefix))
@@ -217,7 +227,9 @@ def _posix_lib(standard_lib, libpython, early_prefix, prefix):
return os.path.join(libpython, "site-packages")
-def get_python_lib(plat_specific=False, standard_lib=False, prefix=None):
+def get_python_lib(
+ plat_specific: bool = False, standard_lib: bool = False, prefix: str | None = None
+) -> str:
"""Return the directory containing the Python library (standard or
site additions).
@@ -232,14 +244,6 @@ def get_python_lib(plat_specific=False, standard_lib=False, prefix=None):
sys.base_exec_prefix -- i.e., ignore 'plat_specific'.
"""
- if IS_PYPY and sys.version_info < (3, 8):
- # PyPy-specific schema
- if prefix is None:
- prefix = PREFIX
- if standard_lib:
- return os.path.join(prefix, "lib-python", sys.version_info.major)
- return os.path.join(prefix, 'site-packages')
-
early_prefix = prefix
if prefix is None:
@@ -288,7 +292,7 @@ def _customize_macos():
)
-def customize_compiler(compiler):
+def customize_compiler(compiler: CCompiler) -> None:
"""Do any platform-specific customization of a CCompiler instance.
Mainly needed on Unix, so we can plug in the information that
@@ -375,12 +379,12 @@ def customize_compiler(compiler):
compiler.shared_lib_extension = shlib_suffix
-def get_config_h_filename():
+def get_config_h_filename() -> str:
"""Return full pathname of installed pyconfig.h file."""
return sysconfig.get_config_h_filename()
-def get_makefile_filename():
+def get_makefile_filename() -> str:
"""Return full pathname of installed Makefile from the Python build."""
return sysconfig.get_makefile_filename()
@@ -542,7 +546,11 @@ def expand_makefile_vars(s, vars):
_config_vars = None
-def get_config_vars(*args):
+@overload
+def get_config_vars() -> dict[str, str | int]: ...
+@overload
+def get_config_vars(arg: str, /, *args: str) -> list[str | int]: ...
+def get_config_vars(*args: str) -> list[str | int] | dict[str, str | int]:
"""With no arguments, return a dictionary of all configuration
variables relevant for the current platform. Generally this includes
everything needed to build extensions and install both pure modules and
@@ -560,7 +568,14 @@ def get_config_vars(*args):
return [_config_vars.get(name) for name in args] if args else _config_vars
-def get_config_var(name):
+@overload
+@deprecated(
+ "SO is deprecated, use EXT_SUFFIX. Support will be removed when this module is synchronized with stdlib Python 3.11"
+)
+def get_config_var(name: Literal["SO"]) -> int | str | None: ...
+@overload
+def get_config_var(name: str) -> int | str | None: ...
+def get_config_var(name: str) -> int | str | None:
"""Return the value of a single variable using the dictionary
returned by 'get_config_vars()'. Equivalent to
get_config_vars().get(name)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py
index 6c1116ae8f7..20b8ce6b9bb 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/unixccompiler.py
@@ -1,402 +1,9 @@
-"""distutils.unixccompiler
+import importlib
-Contains the UnixCCompiler class, a subclass of CCompiler that handles
-the "typical" Unix-style command-line C compiler:
- * macros defined with -Dname[=value]
- * macros undefined with -Uname
- * include search directories specified with -Idir
- * libraries specified with -lllib
- * library search directories specified with -Ldir
- * compile handled by 'cc' (or similar) executable with -c option:
- compiles .c to .o
- * link static library handled by 'ar' command (possibly with 'ranlib')
- * link shared library handled by 'cc -shared'
-"""
+from .compilers.C import unix
-from __future__ import annotations
+UnixCCompiler = unix.Compiler
-import itertools
-import os
-import re
-import shlex
-import sys
-
-from . import sysconfig
-from ._log import log
-from ._macos_compat import compiler_fixup
-from ._modified import newer
-from .ccompiler import CCompiler, gen_lib_options, gen_preprocess_options
-from .compat import consolidate_linker_args
-from .errors import CompileError, DistutilsExecError, LibError, LinkError
-
-# XXX Things not currently handled:
-# * optimization/debug/warning flags; we just use whatever's in Python's
-# Makefile and live with it. Is this adequate? If not, we might
-# have to have a bunch of subclasses GNUCCompiler, SGICCompiler,
-# SunCCompiler, and I suspect down that road lies madness.
-# * even if we don't know a warning flag from an optimization flag,
-# we need some way for outsiders to feed preprocessor/compiler/linker
-# flags in to us -- eg. a sysadmin might want to mandate certain flags
-# via a site config file, or a user might want to set something for
-# compiling this module distribution only via the setup.py command
-# line, whatever. As long as these options come from something on the
-# current system, they can be as system-dependent as they like, and we
-# should just happily stuff them into the preprocessor/compiler/linker
-# options and carry on.
-
-
-def _split_env(cmd):
- """
- For macOS, split command into 'env' portion (if any)
- and the rest of the linker command.
-
- >>> _split_env(['a', 'b', 'c'])
- ([], ['a', 'b', 'c'])
- >>> _split_env(['/usr/bin/env', 'A=3', 'gcc'])
- (['/usr/bin/env', 'A=3'], ['gcc'])
- """
- pivot = 0
- if os.path.basename(cmd[0]) == "env":
- pivot = 1
- while '=' in cmd[pivot]:
- pivot += 1
- return cmd[:pivot], cmd[pivot:]
-
-
-def _split_aix(cmd):
- """
- AIX platforms prefix the compiler with the ld_so_aix
- script, so split that from the linker command.
-
- >>> _split_aix(['a', 'b', 'c'])
- ([], ['a', 'b', 'c'])
- >>> _split_aix(['/bin/foo/ld_so_aix', 'gcc'])
- (['/bin/foo/ld_so_aix'], ['gcc'])
- """
- pivot = os.path.basename(cmd[0]) == 'ld_so_aix'
- return cmd[:pivot], cmd[pivot:]
-
-
-def _linker_params(linker_cmd, compiler_cmd):
- """
- The linker command usually begins with the compiler
- command (possibly multiple elements), followed by zero or more
- params for shared library building.
-
- If the LDSHARED env variable overrides the linker command,
- however, the commands may not match.
-
- Return the best guess of the linker parameters by stripping
- the linker command. If the compiler command does not
- match the linker command, assume the linker command is
- just the first element.
-
- >>> _linker_params('gcc foo bar'.split(), ['gcc'])
- ['foo', 'bar']
- >>> _linker_params('gcc foo bar'.split(), ['other'])
- ['foo', 'bar']
- >>> _linker_params('ccache gcc foo bar'.split(), 'ccache gcc'.split())
- ['foo', 'bar']
- >>> _linker_params(['gcc'], ['gcc'])
- []
- """
- c_len = len(compiler_cmd)
- pivot = c_len if linker_cmd[:c_len] == compiler_cmd else 1
- return linker_cmd[pivot:]
-
-
-class UnixCCompiler(CCompiler):
- compiler_type = 'unix'
-
- # These are used by CCompiler in two places: the constructor sets
- # instance attributes 'preprocessor', 'compiler', etc. from them, and
- # 'set_executable()' allows any of these to be set. The defaults here
- # are pretty generic; they will probably have to be set by an outsider
- # (eg. using information discovered by the sysconfig about building
- # Python extensions).
- executables = {
- 'preprocessor': None,
- 'compiler': ["cc"],
- 'compiler_so': ["cc"],
- 'compiler_cxx': ["c++"],
- 'compiler_so_cxx': ["c++"],
- 'linker_so': ["cc", "-shared"],
- 'linker_so_cxx': ["c++", "-shared"],
- 'linker_exe': ["cc"],
- 'linker_exe_cxx': ["c++", "-shared"],
- 'archiver': ["ar", "-cr"],
- 'ranlib': None,
- }
-
- if sys.platform[:6] == "darwin":
- executables['ranlib'] = ["ranlib"]
-
- # Needed for the filename generation methods provided by the base
- # class, CCompiler. NB. whoever instantiates/uses a particular
- # UnixCCompiler instance should set 'shared_lib_ext' -- we set a
- # reasonable common default here, but it's not necessarily used on all
- # Unices!
-
- src_extensions = [".c", ".C", ".cc", ".cxx", ".cpp", ".m"]
- obj_extension = ".o"
- static_lib_extension = ".a"
- shared_lib_extension = ".so"
- dylib_lib_extension = ".dylib"
- xcode_stub_lib_extension = ".tbd"
- static_lib_format = shared_lib_format = dylib_lib_format = "lib%s%s"
- xcode_stub_lib_format = dylib_lib_format
- if sys.platform == "cygwin":
- exe_extension = ".exe"
- shared_lib_extension = ".dll.a"
- dylib_lib_extension = ".dll"
- dylib_lib_format = "cyg%s%s"
-
- def preprocess(
- self,
- source,
- output_file=None,
- macros=None,
- include_dirs=None,
- extra_preargs=None,
- extra_postargs=None,
- ):
- fixed_args = self._fix_compile_args(None, macros, include_dirs)
- ignore, macros, include_dirs = fixed_args
- pp_opts = gen_preprocess_options(macros, include_dirs)
- pp_args = self.preprocessor + pp_opts
- if output_file:
- pp_args.extend(['-o', output_file])
- if extra_preargs:
- pp_args[:0] = extra_preargs
- if extra_postargs:
- pp_args.extend(extra_postargs)
- pp_args.append(source)
-
- # reasons to preprocess:
- # - force is indicated
- # - output is directed to stdout
- # - source file is newer than the target
- preprocess = self.force or output_file is None or newer(source, output_file)
- if not preprocess:
- return
-
- if output_file:
- self.mkpath(os.path.dirname(output_file))
-
- try:
- self.spawn(pp_args)
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
- compiler_so = compiler_fixup(self.compiler_so, cc_args + extra_postargs)
- compiler_so_cxx = compiler_fixup(self.compiler_so_cxx, cc_args + extra_postargs)
- try:
- if self.detect_language(src) == 'c++':
- self.spawn(
- compiler_so_cxx + cc_args + [src, '-o', obj] + extra_postargs
- )
- else:
- self.spawn(compiler_so + cc_args + [src, '-o', obj] + extra_postargs)
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=False, target_lang=None
- ):
- objects, output_dir = self._fix_object_args(objects, output_dir)
-
- output_filename = self.library_filename(output_libname, output_dir=output_dir)
-
- if self._need_link(objects, output_filename):
- self.mkpath(os.path.dirname(output_filename))
- self.spawn(self.archiver + [output_filename] + objects + self.objects)
-
- # Not many Unices required ranlib anymore -- SunOS 4.x is, I
- # think the only major Unix that does. Maybe we need some
- # platform intelligence here to skip ranlib if it's not
- # needed -- or maybe Python's configure script took care of
- # it for us, hence the check for leading colon.
- if self.ranlib:
- try:
- self.spawn(self.ranlib + [output_filename])
- except DistutilsExecError as msg:
- raise LibError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def link(
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- objects, output_dir = self._fix_object_args(objects, output_dir)
- fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
- libraries, library_dirs, runtime_library_dirs = fixed_args
-
- lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
- if not isinstance(output_dir, (str, type(None))):
- raise TypeError("'output_dir' must be a string or None")
- if output_dir is not None:
- output_filename = os.path.join(output_dir, output_filename)
-
- if self._need_link(objects, output_filename):
- ld_args = objects + self.objects + lib_opts + ['-o', output_filename]
- if debug:
- ld_args[:0] = ['-g']
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend(extra_postargs)
- self.mkpath(os.path.dirname(output_filename))
- try:
- # Select a linker based on context: linker_exe when
- # building an executable or linker_so (with shared options)
- # when building a shared library.
- building_exe = target_desc == CCompiler.EXECUTABLE
- linker = (
- self.linker_exe
- if building_exe
- else (
- self.linker_so_cxx if target_lang == "c++" else self.linker_so
- )
- )[:]
-
- if target_lang == "c++" and self.compiler_cxx:
- env, linker_ne = _split_env(linker)
- aix, linker_na = _split_aix(linker_ne)
- _, compiler_cxx_ne = _split_env(self.compiler_cxx)
- _, linker_exe_ne = _split_env(self.linker_exe)
-
- params = _linker_params(linker_na, linker_exe_ne)
- linker = env + aix + compiler_cxx_ne + params
-
- linker = compiler_fixup(linker, ld_args)
-
- self.spawn(linker + ld_args)
- except DistutilsExecError as msg:
- raise LinkError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- # -- Miscellaneous methods -----------------------------------------
- # These are all used by the 'gen_lib_options() function, in
- # ccompiler.py.
-
- def library_dir_option(self, dir):
- return "-L" + dir
-
- def _is_gcc(self):
- cc_var = sysconfig.get_config_var("CC")
- compiler = os.path.basename(shlex.split(cc_var)[0])
- return "gcc" in compiler or "g++" in compiler
-
- def runtime_library_dir_option(self, dir: str) -> str | list[str]:
- # XXX Hackish, at the very least. See Python bug #445902:
- # https://bugs.python.org/issue445902
- # Linkers on different platforms need different options to
- # specify that directories need to be added to the list of
- # directories searched for dependencies when a dynamic library
- # is sought. GCC on GNU systems (Linux, FreeBSD, ...) has to
- # be told to pass the -R option through to the linker, whereas
- # other compilers and gcc on other systems just know this.
- # Other compilers may need something slightly different. At
- # this time, there's no way to determine this information from
- # the configuration data stored in the Python installation, so
- # we use this hack.
- if sys.platform[:6] == "darwin":
- from distutils.util import get_macosx_target_ver, split_version
-
- macosx_target_ver = get_macosx_target_ver()
- if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]:
- return "-Wl,-rpath," + dir
- else: # no support for -rpath on earlier macOS versions
- return "-L" + dir
- elif sys.platform[:7] == "freebsd":
- return "-Wl,-rpath=" + dir
- elif sys.platform[:5] == "hp-ux":
- return [
- "-Wl,+s" if self._is_gcc() else "+s",
- "-L" + dir,
- ]
-
- # For all compilers, `-Wl` is the presumed way to pass a
- # compiler option to the linker
- if sysconfig.get_config_var("GNULD") == "yes":
- return consolidate_linker_args([
- # Force RUNPATH instead of RPATH
- "-Wl,--enable-new-dtags",
- "-Wl,-rpath," + dir,
- ])
- else:
- return "-Wl,-R" + dir
-
- def library_option(self, lib):
- return "-l" + lib
-
- @staticmethod
- def _library_root(dir):
- """
- macOS users can specify an alternate SDK using'-isysroot'.
- Calculate the SDK root if it is specified.
-
- Note that, as of Xcode 7, Apple SDKs may contain textual stub
- libraries with .tbd extensions rather than the normal .dylib
- shared libraries installed in /. The Apple compiler tool
- chain handles this transparently but it can cause problems
- for programs that are being built with an SDK and searching
- for specific libraries. Callers of find_library_file need to
- keep in mind that the base filename of the returned SDK library
- file might have a different extension from that of the library
- file installed on the running system, for example:
- /Applications/Xcode.app/Contents/Developer/Platforms/
- MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
- usr/lib/libedit.tbd
- vs
- /usr/lib/libedit.dylib
- """
- cflags = sysconfig.get_config_var('CFLAGS')
- match = re.search(r'-isysroot\s*(\S+)', cflags)
-
- apply_root = (
- sys.platform == 'darwin'
- and match
- and (
- dir.startswith('/System/')
- or (dir.startswith('/usr/') and not dir.startswith('/usr/local/'))
- )
- )
-
- return os.path.join(match.group(1), dir[1:]) if apply_root else dir
-
- def find_library_file(self, dirs, lib, debug=False):
- """
- Second-guess the linker with not much hard
- data to go on: GCC seems to prefer the shared library, so
- assume that *all* Unix C compilers do,
- ignoring even GCC's "-static" option.
- """
- lib_names = (
- self.library_filename(lib, lib_type=type)
- for type in 'dylib xcode_stub shared static'.split()
- )
-
- roots = map(self._library_root, dirs)
-
- searched = itertools.starmap(os.path.join, itertools.product(roots, lib_names))
-
- found = filter(os.path.exists, searched)
-
- # Return None if it could not be found in any dir.
- return next(found, None)
+# ensure import of unixccompiler implies ccompiler imported
+# (pypa/setuptools#4871)
+importlib.import_module('distutils.ccompiler')
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/util.py b/contrib/python/setuptools/py3/setuptools/_distutils/util.py
index 83ad39e958d..6dbe049f427 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/util.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/util.py
@@ -16,6 +16,8 @@ import subprocess
import sys
import sysconfig
import tempfile
+from collections.abc import Callable, Iterable, Mapping
+from typing import TYPE_CHECKING, AnyStr
from jaraco.functools import pass_none
@@ -24,6 +26,11 @@ from ._modified import newer
from .errors import DistutilsByteCompileError, DistutilsPlatformError
from .spawn import spawn
+if TYPE_CHECKING:
+ from typing_extensions import TypeVarTuple, Unpack
+
+ _Ts = TypeVarTuple("_Ts")
+
def get_host_platform() -> str:
"""
@@ -39,7 +46,7 @@ def get_host_platform() -> str:
return sysconfig.get_platform()
-def get_platform():
+def get_platform() -> str:
if os.name == 'nt':
TARGET_TO_PLAT = {
'x86': 'win32',
@@ -108,13 +115,13 @@ def get_macosx_target_ver():
return syscfg_ver
-def split_version(s):
+def split_version(s: str) -> list[int]:
"""Convert a dot-separated string into a list of numbers for comparisons"""
return [int(n) for n in s.split('.')]
@pass_none
-def convert_path(pathname: str | os.PathLike) -> str:
+def convert_path(pathname: str | os.PathLike[str]) -> str:
r"""
Allow for pathlib.Path inputs, coax to a native path string.
@@ -132,7 +139,9 @@ def convert_path(pathname: str | os.PathLike) -> str:
return os.fspath(pathlib.PurePath(pathname))
-def change_root(new_root, pathname):
+def change_root(
+ new_root: AnyStr | os.PathLike[AnyStr], pathname: AnyStr | os.PathLike[AnyStr]
+) -> AnyStr:
"""Return 'pathname' with 'new_root' prepended. If 'pathname' is
relative, this is equivalent to "os.path.join(new_root,pathname)".
Otherwise, it requires making 'pathname' relative and then joining the
@@ -154,7 +163,7 @@ def change_root(new_root, pathname):
@functools.lru_cache
-def check_environ():
+def check_environ() -> None:
"""Ensure that 'os.environ' has all the environment variables we
guarantee that users can use in config files, command-line options,
etc. Currently this includes:
@@ -176,7 +185,7 @@ def check_environ():
os.environ['PLAT'] = get_platform()
-def subst_vars(s, local_vars):
+def subst_vars(s, local_vars: Mapping[str, object]) -> str:
"""
Perform variable substitution on 'string'.
Variables are indicated by format-style braces ("{var}").
@@ -215,7 +224,7 @@ def _subst_compat(s):
return repl
-def grok_environment_error(exc, prefix="error: "):
+def grok_environment_error(exc: object, prefix: str = "error: ") -> str:
# Function kept for backward compatibility.
# Used to try clever things with EnvironmentErrors,
# but nowadays str(exception) produces good messages.
@@ -233,7 +242,7 @@ def _init_regex():
_dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"')
-def split_quoted(s):
+def split_quoted(s: str) -> list[str]:
"""Split a string up according to Unix shell-like rules for quotes and
backslashes. In short: words are delimited by spaces, as long as those
spaces are not escaped by a backslash, or inside a quoted string.
@@ -299,14 +308,21 @@ def split_quoted(s):
# split_quoted ()
-def execute(func, args, msg=None, verbose=False, dry_run=False):
- """Perform some action that affects the outside world (eg. by
- writing to the filesystem). Such actions are special because they
- are disabled by the 'dry_run' flag. This method takes care of all
- that bureaucracy for you; all you have to do is supply the
+def execute(
+ func: Callable[[Unpack[_Ts]], object],
+ args: tuple[Unpack[_Ts]],
+ msg: object = None,
+ verbose: bool = False,
+ dry_run: bool = False,
+) -> None:
+ """
+ Perform some action that affects the outside world (e.g. by
+ writing to the filesystem). Such actions are special because they
+ are disabled by the 'dry_run' flag. This method handles that
+ complication; simply supply the
function to call and an argument tuple for it (to embody the
- "external action" being performed), and an optional message to
- print.
+ "external action" being performed) and an optional message to
+ emit.
"""
if msg is None:
msg = f"{func.__name__}{args!r}"
@@ -318,7 +334,7 @@ def execute(func, args, msg=None, verbose=False, dry_run=False):
func(*args)
-def strtobool(val):
+def strtobool(val: str) -> bool:
"""Convert a string representation of truth to true (1) or false (0).
True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values
@@ -327,23 +343,23 @@ def strtobool(val):
"""
val = val.lower()
if val in ('y', 'yes', 't', 'true', 'on', '1'):
- return 1
+ return True
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
- return 0
+ return False
else:
raise ValueError(f"invalid truth value {val!r}")
def byte_compile( # noqa: C901
- py_files,
- optimize=0,
- force=False,
- prefix=None,
- base_dir=None,
- verbose=True,
- dry_run=False,
- direct=None,
-):
+ py_files: Iterable[str],
+ optimize: int = 0,
+ force: bool = False,
+ prefix: str | None = None,
+ base_dir: str | None = None,
+ verbose: bool = True,
+ dry_run: bool = False,
+ direct: bool | None = None,
+) -> None:
"""Byte-compile a collection of Python source files to .pyc
files in a __pycache__ subdirectory. 'py_files' is a list
of files to compile; any files that don't end in ".py" are silently
@@ -473,7 +489,7 @@ byte_compile(files, optimize={optimize!r}, force={force!r},
log.debug("skipping byte-compilation of %s to %s", file, cfile_base)
-def rfc822_escape(header):
+def rfc822_escape(header: str) -> str:
"""Return a version of the string escaped for inclusion in an
RFC-822 header, by ensuring there are 8 spaces space after each newline.
"""
@@ -488,7 +504,7 @@ def rfc822_escape(header):
return indent.join(lines) + suffix
-def is_mingw():
+def is_mingw() -> bool:
"""Returns True if the current platform is mingw.
Python compiled with Mingw-w64 has sys.platform == 'win32' and
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/zosccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/zosccompiler.py
index af1e7fa5cce..e49630ac6ed 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/zosccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/zosccompiler.py
@@ -1,229 +1,3 @@
-"""distutils.zosccompiler
+from .compilers.C import zos
-Contains the selection of the c & c++ compilers on z/OS. There are several
-different c compilers on z/OS, all of them are optional, so the correct
-one needs to be chosen based on the users input. This is compatible with
-the following compilers:
-
-IBM C/C++ For Open Enterprise Languages on z/OS 2.0
-IBM Open XL C/C++ 1.1 for z/OS
-IBM XL C/C++ V2.4.1 for z/OS 2.4 and 2.5
-IBM z/OS XL C/C++
-"""
-
-import os
-
-from . import sysconfig
-from .errors import CompileError, DistutilsExecError
-from .unixccompiler import UnixCCompiler
-
-_cc_args = {
- 'ibm-openxl': [
- '-m64',
- '-fvisibility=default',
- '-fzos-le-char-mode=ascii',
- '-fno-short-enums',
- ],
- 'ibm-xlclang': [
- '-q64',
- '-qexportall',
- '-qascii',
- '-qstrict',
- '-qnocsect',
- '-Wa,asa,goff',
- '-Wa,xplink',
- '-qgonumber',
- '-qenum=int',
- '-Wc,DLL',
- ],
- 'ibm-xlc': [
- '-q64',
- '-qexportall',
- '-qascii',
- '-qstrict',
- '-qnocsect',
- '-Wa,asa,goff',
- '-Wa,xplink',
- '-qgonumber',
- '-qenum=int',
- '-Wc,DLL',
- '-qlanglvl=extc99',
- ],
-}
-
-_cxx_args = {
- 'ibm-openxl': [
- '-m64',
- '-fvisibility=default',
- '-fzos-le-char-mode=ascii',
- '-fno-short-enums',
- ],
- 'ibm-xlclang': [
- '-q64',
- '-qexportall',
- '-qascii',
- '-qstrict',
- '-qnocsect',
- '-Wa,asa,goff',
- '-Wa,xplink',
- '-qgonumber',
- '-qenum=int',
- '-Wc,DLL',
- ],
- 'ibm-xlc': [
- '-q64',
- '-qexportall',
- '-qascii',
- '-qstrict',
- '-qnocsect',
- '-Wa,asa,goff',
- '-Wa,xplink',
- '-qgonumber',
- '-qenum=int',
- '-Wc,DLL',
- '-qlanglvl=extended0x',
- ],
-}
-
-_asm_args = {
- 'ibm-openxl': ['-fasm', '-fno-integrated-as', '-Wa,--ASA', '-Wa,--GOFF'],
- 'ibm-xlclang': [],
- 'ibm-xlc': [],
-}
-
-_ld_args = {
- 'ibm-openxl': [],
- 'ibm-xlclang': ['-Wl,dll', '-q64'],
- 'ibm-xlc': ['-Wl,dll', '-q64'],
-}
-
-
-# Python on z/OS is built with no compiler specific options in it's CFLAGS.
-# But each compiler requires it's own specific options to build successfully,
-# though some of the options are common between them
-class zOSCCompiler(UnixCCompiler):
- src_extensions = ['.c', '.C', '.cc', '.cxx', '.cpp', '.m', '.s']
- _cpp_extensions = ['.cc', '.cpp', '.cxx', '.C']
- _asm_extensions = ['.s']
-
- def _get_zos_compiler_name(self):
- zos_compiler_names = [
- os.path.basename(binary)
- for envvar in ('CC', 'CXX', 'LDSHARED')
- if (binary := os.environ.get(envvar, None))
- ]
- if len(zos_compiler_names) == 0:
- return 'ibm-openxl'
-
- zos_compilers = {}
- for compiler in (
- 'ibm-clang',
- 'ibm-clang64',
- 'ibm-clang++',
- 'ibm-clang++64',
- 'clang',
- 'clang++',
- 'clang-14',
- ):
- zos_compilers[compiler] = 'ibm-openxl'
-
- for compiler in ('xlclang', 'xlclang++', 'njsc', 'njsc++'):
- zos_compilers[compiler] = 'ibm-xlclang'
-
- for compiler in ('xlc', 'xlC', 'xlc++'):
- zos_compilers[compiler] = 'ibm-xlc'
-
- return zos_compilers.get(zos_compiler_names[0], 'ibm-openxl')
-
- def __init__(self, verbose=False, dry_run=False, force=False):
- super().__init__(verbose, dry_run, force)
- self.zos_compiler = self._get_zos_compiler_name()
- sysconfig.customize_compiler(self)
-
- def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
- local_args = []
- if ext in self._cpp_extensions:
- compiler = self.compiler_cxx
- local_args.extend(_cxx_args[self.zos_compiler])
- elif ext in self._asm_extensions:
- compiler = self.compiler_so
- local_args.extend(_cc_args[self.zos_compiler])
- local_args.extend(_asm_args[self.zos_compiler])
- else:
- compiler = self.compiler_so
- local_args.extend(_cc_args[self.zos_compiler])
- local_args.extend(cc_args)
-
- try:
- self.spawn(compiler + local_args + [src, '-o', obj] + extra_postargs)
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- def runtime_library_dir_option(self, dir):
- return '-L' + dir
-
- def link(
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- # For a built module to use functions from cpython, it needs to use Pythons
- # side deck file. The side deck is located beside the libpython3.xx.so
- ldversion = sysconfig.get_config_var('LDVERSION')
- if sysconfig.python_build:
- side_deck_path = os.path.join(
- sysconfig.get_config_var('abs_builddir'),
- f'libpython{ldversion}.x',
- )
- else:
- side_deck_path = os.path.join(
- sysconfig.get_config_var('installed_base'),
- sysconfig.get_config_var('platlibdir'),
- f'libpython{ldversion}.x',
- )
-
- if os.path.exists(side_deck_path):
- if extra_postargs:
- extra_postargs.append(side_deck_path)
- else:
- extra_postargs = [side_deck_path]
-
- # Check and replace libraries included side deck files
- if runtime_library_dirs:
- for dir in runtime_library_dirs:
- for library in libraries[:]:
- library_side_deck = os.path.join(dir, f'{library}.x')
- if os.path.exists(library_side_deck):
- libraries.remove(library)
- extra_postargs.append(library_side_deck)
- break
-
- # Any required ld args for the given compiler
- extra_postargs.extend(_ld_args[self.zos_compiler])
-
- super().link(
- target_desc,
- objects,
- output_filename,
- output_dir,
- libraries,
- library_dirs,
- runtime_library_dirs,
- export_symbols,
- debug,
- extra_preargs,
- extra_postargs,
- build_temp,
- target_lang,
- )
+zOSCCompiler = zos.Compiler
diff --git a/contrib/python/setuptools/py3/setuptools/_normalization.py b/contrib/python/setuptools/py3/setuptools/_normalization.py
index 9541a55d6c9..0937a4faf8c 100644
--- a/contrib/python/setuptools/py3/setuptools/_normalization.py
+++ b/contrib/python/setuptools/py3/setuptools/_normalization.py
@@ -4,6 +4,7 @@ and core metadata
"""
import re
+from typing import TYPE_CHECKING
import packaging
@@ -148,3 +149,31 @@ def safer_best_effort_version(value: str) -> str:
# See bdist_wheel.safer_verion
# TODO: Replace with only safe_version in the future (no need for best effort)
return filename_component(best_effort_version(value))
+
+
+def _missing_canonicalize_license_expression(expression: str) -> str:
+ """
+ Defer import error to affect only users that actually use it
+ https://github.com/pypa/setuptools/issues/4894
+ >>> _missing_canonicalize_license_expression("a OR b")
+ Traceback (most recent call last):
+ ...
+ ImportError: ...Cannot import `packaging.licenses`...
+ """
+ raise ImportError(
+ "Cannot import `packaging.licenses`."
+ """
+ Setuptools>=77.0.0 requires "packaging>=24.2" to work properly.
+ Please make sure you have a suitable version installed.
+ """
+ )
+
+
+try:
+ from packaging.licenses import (
+ canonicalize_license_expression as _canonicalize_license_expression,
+ )
+except ImportError: # pragma: nocover
+ if not TYPE_CHECKING:
+ # XXX: pyright is still upset even with # pyright: ignore[reportAssignmentType]
+ _canonicalize_license_expression = _missing_canonicalize_license_expression
diff --git a/contrib/python/setuptools/py3/setuptools/_static.py b/contrib/python/setuptools/py3/setuptools/_static.py
index 075a0bcddf3..af35862cf8b 100644
--- a/contrib/python/setuptools/py3/setuptools/_static.py
+++ b/contrib/python/setuptools/py3/setuptools/_static.py
@@ -27,7 +27,7 @@ class Static:
def _prevent_modification(target: type, method: str, copying: str) -> None:
"""
Because setuptools is very flexible we cannot fully prevent
- plugins and user customisations from modifying static values that were
+ plugins and user customizations from modifying static values that were
parsed from config files.
But we can attempt to block "in-place" mutations and identify when they
were done.
diff --git a/contrib/python/setuptools/py3/setuptools/build_meta.py b/contrib/python/setuptools/py3/setuptools/build_meta.py
index 00fa5e1f704..8f2e930c734 100644
--- a/contrib/python/setuptools/py3/setuptools/build_meta.py
+++ b/contrib/python/setuptools/py3/setuptools/build_meta.py
@@ -67,9 +67,6 @@ __all__ = [
'SetupRequirementsError',
]
-SETUPTOOLS_ENABLE_FEATURES = os.getenv("SETUPTOOLS_ENABLE_FEATURES", "").lower()
-LEGACY_EDITABLE = "legacy-editable" in SETUPTOOLS_ENABLE_FEATURES.replace("_", "-")
-
class SetupRequirementsError(BaseException):
def __init__(self, specifiers) -> None:
@@ -457,37 +454,30 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
assert len(dist_info_candidates) <= 1
return str(dist_info_candidates[0]) if dist_info_candidates else None
- if not LEGACY_EDITABLE:
- # PEP660 hooks:
- # build_editable
- # get_requires_for_build_editable
- # prepare_metadata_for_build_editable
- def build_editable(
- self,
- wheel_directory: StrPath,
- config_settings: _ConfigSettings = None,
- metadata_directory: StrPath | None = None,
- ):
- # XXX can or should we hide our editable_wheel command normally?
- info_dir = self._get_dist_info_dir(metadata_directory)
- opts = ["--dist-info-dir", info_dir] if info_dir else []
- cmd = ["editable_wheel", *opts, *self._editable_args(config_settings)]
- with suppress_known_deprecation():
- return self._build_with_temp_dir(
- cmd, ".whl", wheel_directory, config_settings
- )
+ def build_editable(
+ self,
+ wheel_directory: StrPath,
+ config_settings: _ConfigSettings = None,
+ metadata_directory: StrPath | None = None,
+ ):
+ # XXX can or should we hide our editable_wheel command normally?
+ info_dir = self._get_dist_info_dir(metadata_directory)
+ opts = ["--dist-info-dir", info_dir] if info_dir else []
+ cmd = ["editable_wheel", *opts, *self._editable_args(config_settings)]
+ with suppress_known_deprecation():
+ return self._build_with_temp_dir(
+ cmd, ".whl", wheel_directory, config_settings
+ )
- def get_requires_for_build_editable(
- self, config_settings: _ConfigSettings = None
- ):
- return self.get_requires_for_build_wheel(config_settings)
+ def get_requires_for_build_editable(self, config_settings: _ConfigSettings = None):
+ return self.get_requires_for_build_wheel(config_settings)
- def prepare_metadata_for_build_editable(
- self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
- ):
- return self.prepare_metadata_for_build_wheel(
- metadata_directory, config_settings
- )
+ def prepare_metadata_for_build_editable(
+ self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
+ ):
+ return self.prepare_metadata_for_build_wheel(
+ metadata_directory, config_settings
+ )
class _BuildMetaLegacyBackend(_BuildMetaBackend):
@@ -549,11 +539,9 @@ get_requires_for_build_sdist = _BACKEND.get_requires_for_build_sdist
prepare_metadata_for_build_wheel = _BACKEND.prepare_metadata_for_build_wheel
build_wheel = _BACKEND.build_wheel
build_sdist = _BACKEND.build_sdist
-
-if not LEGACY_EDITABLE:
- get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable
- prepare_metadata_for_build_editable = _BACKEND.prepare_metadata_for_build_editable
- build_editable = _BACKEND.build_editable
+get_requires_for_build_editable = _BACKEND.get_requires_for_build_editable
+prepare_metadata_for_build_editable = _BACKEND.prepare_metadata_for_build_editable
+build_editable = _BACKEND.build_editable
# The legacy backend
diff --git a/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
index 27b36232ec1..1e3f637bcc3 100644
--- a/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
+++ b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
@@ -23,6 +23,7 @@ from packaging import tags, version as _packaging_version
from wheel.wheelfile import WheelFile
from .. import Command, __version__, _shutil
+from .._core_metadata import _safe_license_file
from .._normalization import safer_name
from ..warnings import SetuptoolsDeprecationWarning
from .egg_info import egg_info as egg_info_cls
@@ -580,9 +581,12 @@ class bdist_wheel(Command):
metadata_path = os.path.join(distinfo_path, "METADATA")
shutil.copy(pkginfo_path, metadata_path)
+ licenses_folder_path = os.path.join(distinfo_path, "licenses")
for license_path in self.license_paths:
- filename = os.path.basename(license_path)
- shutil.copy(license_path, os.path.join(distinfo_path, filename))
+ safe_path = _safe_license_file(license_path)
+ dist_info_license_path = os.path.join(licenses_folder_path, safe_path)
+ os.makedirs(os.path.dirname(dist_info_license_path), exist_ok=True)
+ shutil.copy(license_path, dist_info_license_path)
adios(egginfo_path)
diff --git a/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
index b03e677757a..1a544ec2581 100644
--- a/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
+++ b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
@@ -278,7 +278,7 @@ class editable_wheel(Command):
This method implements a temporary workaround to support the ecosystem
while the implementations catch up.
"""
- # TODO: Once plugins/customisations had the chance to catch up, replace
+ # TODO: Once plugins/customizations had the chance to catch up, replace
# `self._run_build_subcommands()` with `self.run_command("build")`.
# Also remove _safely_run, TestCustomBuildPy. Suggested date: Aug/2023.
build = self.get_finalized_command("build")
@@ -309,7 +309,7 @@ class editable_wheel(Command):
https://setuptools.pypa.io/en/latest/userguide/extension.html.
For the time being `setuptools` will silence this error and ignore
- the faulty command, but this behaviour will change in future versions.
+ the faulty command, but this behavior will change in future versions.
""",
# TODO: define due_date
# There is a series of shortcomings with the available editable install
@@ -564,7 +564,7 @@ def _encode_pth(content: str) -> bytes:
.pth files are always read with 'locale' encoding, the recommendation
from the cpython core developers is to write them as ``open(path, "w")``
and ignore warnings (see python/cpython#77102, pypa/setuptools#3937).
- This function tries to simulate this behaviour without having to create an
+ This function tries to simulate this behavior 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)``
diff --git a/contrib/python/setuptools/py3/setuptools/command/install.py b/contrib/python/setuptools/py3/setuptools/command/install.py
index 741b140c702..15ef3646888 100644
--- a/contrib/python/setuptools/py3/setuptools/command/install.py
+++ b/contrib/python/setuptools/py3/setuptools/command/install.py
@@ -68,7 +68,7 @@ class install(orig.install):
""",
see_url="https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html",
# TODO: Document how to bootstrap setuptools without install
- # (e.g. by unziping the wheel file)
+ # (e.g. by unzipping the wheel file)
# and then add a due_date to this warning.
)
diff --git a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
index 331596bdd79..9088bc1383c 100644
--- a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
+++ b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
@@ -22,9 +22,9 @@ from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union
from .. import _static
from .._path import StrPath
-from ..errors import RemovedConfigError
+from ..errors import InvalidConfigError, RemovedConfigError
from ..extension import Extension
-from ..warnings import SetuptoolsWarning
+from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning
if TYPE_CHECKING:
from typing_extensions import TypeAlias
@@ -58,6 +58,7 @@ def apply(dist: Distribution, config: dict, filename: StrPath) -> Distribution:
os.chdir(root_dir)
try:
dist._finalize_requires()
+ dist._finalize_license_expression()
dist._finalize_license_files()
finally:
os.chdir(current_directory)
@@ -88,6 +89,22 @@ def _apply_tool_table(dist: Distribution, config: dict, filename: StrPath):
if not tool_table:
return # short-circuit
+ if "license-files" in tool_table:
+ if "license-files" in config.get("project", {}):
+ # https://github.com/pypa/setuptools/pull/4837#discussion_r2004983349
+ raise InvalidConfigError(
+ "'project.license-files' is defined already. "
+ "Remove 'tool.setuptools.license-files'."
+ )
+
+ pypa_guides = "guides/writing-pyproject-toml/#license-files"
+ SetuptoolsDeprecationWarning.emit(
+ "'tool.setuptools.license-files' is deprecated in favor of "
+ "'project.license-files' (available on setuptools>=77.0.0).",
+ see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+ due_date=(2026, 2, 18), # Warning introduced on 2025-02-18
+ )
+
for field, value in tool_table.items():
norm_key = json_compatible_key(field)
@@ -181,16 +198,31 @@ def _long_description(
dist._referenced_files.add(file)
-def _license(dist: Distribution, val: dict, root_dir: StrPath | None):
+def _license(dist: Distribution, val: str | dict, root_dir: StrPath | None):
from setuptools.config import expand
- if "file" in val:
- # XXX: Is it completely safe to assume static?
- value = expand.read_files([val["file"]], root_dir)
- _set_config(dist, "license", _static.Str(value))
- dist._referenced_files.add(val["file"])
+ if isinstance(val, str):
+ if getattr(dist.metadata, "license", None):
+ SetuptoolsWarning.emit("`license` overwritten by `pyproject.toml`")
+ dist.metadata.license = None
+ _set_config(dist, "license_expression", _static.Str(val))
else:
- _set_config(dist, "license", _static.Str(val["text"]))
+ pypa_guides = "guides/writing-pyproject-toml/#license"
+ SetuptoolsDeprecationWarning.emit(
+ "`project.license` as a TOML table is deprecated",
+ "Please use a simple string containing a SPDX expression for "
+ "`project.license`. You can also use `project.license-files`. "
+ "(Both options available on setuptools>=77.0.0).",
+ see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+ due_date=(2026, 2, 18), # Introduced on 2025-02-18
+ )
+ if "file" in val:
+ # XXX: Is it completely safe to assume static?
+ value = expand.read_files([val["file"]], root_dir)
+ _set_config(dist, "license", _static.Str(value))
+ dist._referenced_files.add(val["file"])
+ else:
+ _set_config(dist, "license", _static.Str(val["text"]))
def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind: str):
@@ -419,6 +451,7 @@ SETUPTOOLS_PATCHES = {
"provides_extras",
"license_file",
"license_files",
+ "license_expression",
}
_PREPROCESS = {
@@ -431,7 +464,9 @@ _PREVIOUSLY_DEFINED = {
"description": _attrgetter("metadata.description"),
"readme": _attrgetter("metadata.long_description"),
"requires-python": _some_attrgetter("python_requires", "metadata.python_requires"),
- "license": _attrgetter("metadata.license"),
+ "license": _some_attrgetter("metadata.license_expression", "metadata.license"),
+ # XXX: `license-file` is currently not considered in the context of `dynamic`.
+ # See TestPresetField.test_license_files_exempt_from_dynamic
"authors": _some_attrgetter("metadata.author", "metadata.author_email"),
"maintainers": _some_attrgetter("metadata.maintainer", "metadata.maintainer_email"),
"keywords": _attrgetter("metadata.keywords"),
@@ -447,8 +482,11 @@ _PREVIOUSLY_DEFINED = {
_RESET_PREVIOUSLY_DEFINED: dict = {
# Fix improper setting: given in `setup.py`, but not listed in `dynamic`
+ # Use "immutable" data structures to avoid in-place modification.
# dict: pyproject name => value to which reset
- "license": _static.EMPTY_DICT,
+ "license": "",
+ # XXX: `license-file` is currently not considered in the context of `dynamic`.
+ # See TestPresetField.test_license_files_exempt_from_dynamic
"authors": _static.EMPTY_LIST,
"maintainers": _static.EMPTY_LIST,
"keywords": _static.EMPTY_LIST,
diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE
index 74e8821fc84..ac5464d88c6 100644
--- a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE
+++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/NOTICE
@@ -1,7 +1,7 @@
The code contained in this directory was automatically generated using the
following command:
- python -m validate_pyproject.pre_compile --output-dir=setuptools/config/_validate_pyproject --enable-plugins setuptools distutils --very-verbose -t distutils=setuptools/config/distutils.schema.json -t setuptools=setuptools/config/setuptools.schema.json
+ python -m validate_pyproject.pre_compile --output-dir=setuptools/config/_validate_pyproject --enable-plugins setuptools distutils --very-verbose -t setuptools=setuptools/config/setuptools.schema.json -t distutils=setuptools/config/distutils.schema.json
Please avoid changing it manually.
diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/extra_validations.py b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/extra_validations.py
index c4ffe651dd0..789411d0ff3 100644
--- a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/extra_validations.py
+++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/extra_validations.py
@@ -24,6 +24,13 @@ class RedefiningStaticFieldAsDynamic(ValidationError):
)
+class IncludedDependencyGroupMustExist(ValidationError):
+ _DESC = """An included dependency group must exist and must not be cyclic.
+ """
+ __doc__ = _DESC
+ _URL = "https://peps.python.org/pep-0735/"
+
+
def validate_project_dynamic(pyproject: T) -> T:
project_table = pyproject.get("project", {})
dynamic = project_table.get("dynamic", [])
@@ -49,4 +56,27 @@ def validate_project_dynamic(pyproject: T) -> T:
return pyproject
-EXTRA_VALIDATIONS = (validate_project_dynamic,)
+def validate_include_depenency(pyproject: T) -> T:
+ dependency_groups = pyproject.get("dependency-groups", {})
+ for key, value in dependency_groups.items():
+ for each in value:
+ if (
+ isinstance(each, dict)
+ and (include_group := each.get("include-group"))
+ and include_group not in dependency_groups
+ ):
+ raise IncludedDependencyGroupMustExist(
+ message=f"The included dependency group {include_group} doesn't exist",
+ value=each,
+ name=f"data.dependency_groups.{key}",
+ definition={
+ "description": cleandoc(IncludedDependencyGroupMustExist._DESC),
+ "see": IncludedDependencyGroupMustExist._URL,
+ },
+ rule="PEP 735",
+ )
+ # TODO: check for `include-group` cycles (can be conditional to graphlib)
+ return pyproject
+
+
+EXTRA_VALIDATIONS = (validate_project_dynamic, validate_include_depenency)
diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py
index 42e7aa5e337..c69368a83ff 100644
--- a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py
+++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/fastjsonschema_validations.py
@@ -17,6 +17,7 @@ from .fastjsonschema_exceptions import JsonSchemaValueException
REGEX_PATTERNS = {
+ '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': re.compile('^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])\\Z'),
'^.*$': re.compile('^.*$'),
'.+': re.compile('.+'),
'^.+$': re.compile('^.+$'),
@@ -31,7 +32,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/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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], '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', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, '$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-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#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', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], '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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'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']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], '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', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, '$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-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#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', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], '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']}}}}}}}}}, 'dependency-groups': {'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}}, '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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'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']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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())
@@ -98,8 +99,59 @@ def validate_https___packaging_python_org_en_latest_specifications_declaring_bui
data__tool_keys.remove("setuptools")
data__tool__setuptools = 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 "dependency-groups" in data_keys:
+ data_keys.remove("dependency-groups")
+ data__dependencygroups = data["dependency-groups"]
+ if not isinstance(data__dependencygroups, (dict)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups must be object", value=data__dependencygroups, name="" + (name_prefix or "data") + ".dependency-groups", definition={'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}, rule='type')
+ data__dependencygroups_is_dict = isinstance(data__dependencygroups, dict)
+ if data__dependencygroups_is_dict:
+ data__dependencygroups_keys = set(data__dependencygroups.keys())
+ for data__dependencygroups_key, data__dependencygroups_val in data__dependencygroups.items():
+ if REGEX_PATTERNS['^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'].search(data__dependencygroups_key):
+ if data__dependencygroups_key in data__dependencygroups_keys:
+ data__dependencygroups_keys.remove(data__dependencygroups_key)
+ if not isinstance(data__dependencygroups_val, (list, tuple)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}".format(**locals()) + " must be array", value=data__dependencygroups_val, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}".format(**locals()) + "", definition={'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}, rule='type')
+ data__dependencygroups_val_is_list = isinstance(data__dependencygroups_val, (list, tuple))
+ if data__dependencygroups_val_is_list:
+ data__dependencygroups_val_len = len(data__dependencygroups_val)
+ for data__dependencygroups_val_x, data__dependencygroups_val_item in enumerate(data__dependencygroups_val):
+ data__dependencygroups_val_item_one_of_count1 = 0
+ if data__dependencygroups_val_item_one_of_count1 < 2:
+ try:
+ if not isinstance(data__dependencygroups_val_item, (str)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be string", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, rule='type')
+ if isinstance(data__dependencygroups_val_item, str):
+ if not custom_formats["pep508"](data__dependencygroups_val_item):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be pep508", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, rule='format')
+ data__dependencygroups_val_item_one_of_count1 += 1
+ except JsonSchemaValueException: pass
+ if data__dependencygroups_val_item_one_of_count1 < 2:
+ try:
+ if not isinstance(data__dependencygroups_val_item, (dict)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be object", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}, rule='type')
+ data__dependencygroups_val_item_is_dict = isinstance(data__dependencygroups_val_item, dict)
+ if data__dependencygroups_val_item_is_dict:
+ data__dependencygroups_val_item_keys = set(data__dependencygroups_val_item.keys())
+ if "include-group" in data__dependencygroups_val_item_keys:
+ data__dependencygroups_val_item_keys.remove("include-group")
+ data__dependencygroups_val_item__includegroup = data__dependencygroups_val_item["include-group"]
+ if not isinstance(data__dependencygroups_val_item__includegroup, (str)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + " must be string", value=data__dependencygroups_val_item__includegroup, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + "", definition={'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}, rule='type')
+ if isinstance(data__dependencygroups_val_item__includegroup, str):
+ if not REGEX_PATTERNS['^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'].search(data__dependencygroups_val_item__includegroup):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + " must match pattern ^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$", value=data__dependencygroups_val_item__includegroup, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}].include-group".format(**locals()) + "", definition={'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}, rule='pattern')
+ if data__dependencygroups_val_item_keys:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must not contain "+str(data__dependencygroups_val_item_keys)+" properties", value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}, rule='additionalProperties')
+ data__dependencygroups_val_item_one_of_count1 += 1
+ except JsonSchemaValueException: pass
+ if data__dependencygroups_val_item_one_of_count1 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + " must be valid exactly by one definition" + (" (" + str(data__dependencygroups_val_item_one_of_count1) + " matches found)"), value=data__dependencygroups_val_item, name="" + (name_prefix or "data") + ".dependency-groups.{data__dependencygroups_key}[{data__dependencygroups_val_x}]".format(**locals()) + "", definition={'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}, rule='oneOf')
+ if data__dependencygroups_keys:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dependency-groups must not contain "+str(data__dependencygroups_keys)+" properties", value=data__dependencygroups, name="" + (name_prefix or "data") + ".dependency-groups", definition={'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}, 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://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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], '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', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, '$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-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#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', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], '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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'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']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://setuptools.pypa.io/en/latest/deprecated/distutils/configfile.html>`_.', 'See also `the old Python docs <https://docs.python.org/3.11/install/>_`.'], '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', '<https://setuptools.pypa.io/en/latest/references/keywords.html>`_', 'used by the ``setup.py`` file, and can be set via the ``tool.setuptools`` table.', 'It considers only ``setuptools`` `parameters', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#setuptools-specific-configuration>`_', '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, '$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-relaxed'}, '$comment': 'TODO: clarify the relationship with ``packages``'}, 'ext-modules': {'description': 'Extension modules to be compiled by setuptools', 'type': 'array', 'items': {'$ref': '#/definitions/ext-module'}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/datafiles.html>`_.'], '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-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, 'ext-module': {'$id': '#/definitions/ext-module', 'title': 'Extension module', 'description': 'Parameters to construct a :class:`setuptools.Extension` object', 'type': 'object', 'required': ['name', 'sources'], 'additionalProperties': False, 'properties': {'name': {'type': 'string', 'format': 'python-module-name-relaxed'}, 'sources': {'type': 'array', 'items': {'type': 'string'}}, 'include-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'define-macros': {'type': 'array', 'items': {'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}}, 'undef-macros': {'type': 'array', 'items': {'type': 'string'}}, 'library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'libraries': {'type': 'array', 'items': {'type': 'string'}}, 'runtime-library-dirs': {'type': 'array', 'items': {'type': 'string'}}, 'extra-objects': {'type': 'array', 'items': {'type': 'string'}}, 'extra-compile-args': {'type': 'array', 'items': {'type': 'string'}}, 'extra-link-args': {'type': 'array', 'items': {'type': 'string'}}, 'export-symbols': {'type': 'array', 'items': {'type': 'string'}}, 'swig-opts': {'type': 'array', 'items': {'type': 'string'}}, 'depends': {'type': 'array', 'items': {'type': 'string'}}, 'language': {'type': 'string'}, 'optional': {'type': 'boolean'}, 'py-limited-api': {'type': 'boolean'}}}, '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', '<https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html#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', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], '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']}}}}}}}}}, 'dependency-groups': {'type': 'object', 'description': 'Dependency groups following PEP 735', 'additionalProperties': False, 'patternProperties': {'^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$': {'type': 'array', 'items': {'oneOf': [{'type': 'string', 'description': 'Python package specifiers following PEP 508', 'format': 'pep508'}, {'type': 'object', 'additionalProperties': False, 'properties': {'include-group': {'description': 'Another dependency group to include in this one', 'type': 'string', 'pattern': '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9._-]*[a-zA-Z0-9])$'}}}]}}}}}, '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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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 <https://packaging.python.org/specifications/entry-points/>`_.']}, 'gui-scripts': {'$ref': '#/definitions/entry-point-group', '$$description': ['Instruct the installer to create GUI wrappers for the given', '`entry points <https://packaging.python.org/specifications/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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'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']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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_userguide_pyproject_config_html(data, custom_formats={}, name_prefix=None):
@@ -177,8 +229,8 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
if "packages" in data_keys:
data_keys.remove("packages")
data__packages = data["packages"]
- data__packages_one_of_count1 = 0
- if data__packages_one_of_count1 < 2:
+ data__packages_one_of_count2 = 0
+ if data__packages_one_of_count2 < 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': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}}, rule='type')
@@ -187,15 +239,15 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
data__packages_len = len(data__packages)
for data__packages_x, data__packages_item in enumerate(data__packages):
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
+ data__packages_one_of_count2 += 1
except JsonSchemaValueException: pass
- if data__packages_one_of_count1 < 2:
+ if data__packages_one_of_count2 < 2:
try:
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
+ data__packages_one_of_count2 += 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': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'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', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], '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 data__packages_one_of_count2 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".packages must be valid exactly by one definition" + (" (" + str(data__packages_one_of_count2) + " 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-relaxed'}, {'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', '<https://setuptools.pypa.io/en/latest/userguide/package_discovery.html>`_.'], '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"]
@@ -217,19 +269,19 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
data__packagedir_property_names = True
for data__packagedir_key in data__packagedir:
try:
- data__packagedir_key_any_of_count2 = 0
- if not data__packagedir_key_any_of_count2:
+ data__packagedir_key_any_of_count3 = 0
+ if not data__packagedir_key_any_of_count3:
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_any_of_count2 += 1
+ data__packagedir_key_any_of_count3 += 1
except JsonSchemaValueException: pass
- if not data__packagedir_key_any_of_count2:
+ if not data__packagedir_key_any_of_count3:
try:
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
+ data__packagedir_key_any_of_count3 += 1
except JsonSchemaValueException: pass
- if not data__packagedir_key_any_of_count2:
+ if not data__packagedir_key_any_of_count3:
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-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}]}, rule='anyOf')
except JsonSchemaValueException:
data__packagedir_property_names = False
@@ -262,23 +314,23 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
data__packagedata_property_names = True
for data__packagedata_key in data__packagedata:
try:
- data__packagedata_key_any_of_count3 = 0
- if not data__packagedata_key_any_of_count3:
+ data__packagedata_key_any_of_count4 = 0
+ if not data__packagedata_key_any_of_count4:
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={'type': 'string', 'format': 'python-module-name'}, rule='format')
- data__packagedata_key_any_of_count3 += 1
+ data__packagedata_key_any_of_count4 += 1
except JsonSchemaValueException: pass
- if not data__packagedata_key_any_of_count3:
+ if not data__packagedata_key_any_of_count4:
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_any_of_count3 += 1
+ data__packagedata_key_any_of_count4 += 1
except JsonSchemaValueException: pass
- if not data__packagedata_key_any_of_count3:
+ if not data__packagedata_key_any_of_count4:
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
@@ -316,23 +368,23 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
data__excludepackagedata_property_names = True
for data__excludepackagedata_key in data__excludepackagedata:
try:
- data__excludepackagedata_key_any_of_count4 = 0
- if not data__excludepackagedata_key_any_of_count4:
+ data__excludepackagedata_key_any_of_count5 = 0
+ if not data__excludepackagedata_key_any_of_count5:
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={'type': 'string', 'format': 'python-module-name'}, rule='format')
- data__excludepackagedata_key_any_of_count4 += 1
+ data__excludepackagedata_key_any_of_count5 += 1
except JsonSchemaValueException: pass
- if not data__excludepackagedata_key_any_of_count4:
+ if not data__excludepackagedata_key_any_of_count5:
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_any_of_count4 += 1
+ data__excludepackagedata_key_any_of_count5 += 1
except JsonSchemaValueException: pass
- if not data__excludepackagedata_key_any_of_count4:
+ if not data__excludepackagedata_key_any_of_count5:
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
@@ -435,19 +487,19 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
if "version" in data__dynamic_keys:
data__dynamic_keys.remove("version")
data__dynamic__version = data__dynamic["version"]
- data__dynamic__version_one_of_count5 = 0
- if data__dynamic__version_one_of_count5 < 2:
+ data__dynamic__version_one_of_count6 = 0
+ if data__dynamic__version_one_of_count6 < 2:
try:
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
+ data__dynamic__version_one_of_count6 += 1
except JsonSchemaValueException: pass
- if data__dynamic__version_one_of_count5 < 2:
+ if data__dynamic__version_one_of_count6 < 2:
try:
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
+ data__dynamic__version_one_of_count6 += 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`.', '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 data__dynamic__version_one_of_count6 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic.version must be valid exactly by one definition" + (" (" + str(data__dynamic__version_one_of_count6) + " 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"]
@@ -498,13 +550,13 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
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:
+ data__dynamic__readme_any_of_count7 = 0
+ if not data__dynamic__readme_any_of_count7:
try:
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
+ data__dynamic__readme_any_of_count7 += 1
except JsonSchemaValueException: pass
- if not data__dynamic__readme_any_of_count6:
+ if not data__dynamic__readme_any_of_count7:
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')
@@ -522,9 +574,9 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
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
+ data__dynamic__readme_any_of_count7 += 1
except JsonSchemaValueException: pass
- if not data__dynamic__readme_any_of_count6:
+ if not data__dynamic__readme_any_of_count7:
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:
@@ -538,14 +590,14 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
return data
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:
+ data_one_of_count8 = 0
+ if data_one_of_count8 < 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
+ data_one_of_count8 += 1
except JsonSchemaValueException: pass
- if data_one_of_count7 < 2:
+ if data_one_of_count8 < 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')
@@ -555,10 +607,10 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
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
+ data_one_of_count8 += 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')
+ if data_one_of_count8 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + " must be valid exactly by one definition" + (" (" + str(data_one_of_count8) + " 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):
@@ -577,14 +629,14 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
if "file" in data_keys:
data_keys.remove("file")
data__file = data["file"]
- data__file_one_of_count8 = 0
- if data__file_one_of_count8 < 2:
+ data__file_one_of_count9 = 0
+ if data__file_one_of_count9 < 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_count8 += 1
+ data__file_one_of_count9 += 1
except JsonSchemaValueException: pass
- if data__file_one_of_count8 < 2:
+ if data__file_one_of_count9 < 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')
@@ -594,10 +646,10 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
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_count8 += 1
+ data__file_one_of_count9 += 1
except JsonSchemaValueException: pass
- 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__file_one_of_count9 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".file must be valid exactly by one definition" + (" (" + str(data__file_one_of_count9) + " 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
@@ -682,21 +734,21 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][0]".format(**locals()) + " must be string", value=data__definemacros_item__0, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][0]".format(**locals()) + "", definition={'description': 'macro name', 'type': 'string'}, rule='type')
if data__definemacros_item_len > 1:
data__definemacros_item__1 = data__definemacros_item[1]
- data__definemacros_item__1_one_of_count9 = 0
- if data__definemacros_item__1_one_of_count9 < 2:
+ data__definemacros_item__1_one_of_count10 = 0
+ if data__definemacros_item__1_one_of_count10 < 2:
try:
if not isinstance(data__definemacros_item__1, (str)):
raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be string", value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
- data__definemacros_item__1_one_of_count9 += 1
+ data__definemacros_item__1_one_of_count10 += 1
except JsonSchemaValueException: pass
- if data__definemacros_item__1_one_of_count9 < 2:
+ if data__definemacros_item__1_one_of_count10 < 2:
try:
if not isinstance(data__definemacros_item__1, (NoneType)):
raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be null", value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'type': 'null'}, rule='type')
- data__definemacros_item__1_one_of_count9 += 1
+ data__definemacros_item__1_one_of_count10 += 1
except JsonSchemaValueException: pass
- if data__definemacros_item__1_one_of_count9 != 1:
- raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be valid exactly by one definition" + (" (" + str(data__definemacros_item__1_one_of_count9) + " matches found)"), value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}, rule='oneOf')
+ if data__definemacros_item__1_one_of_count10 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + " must be valid exactly by one definition" + (" (" + str(data__definemacros_item__1_one_of_count10) + " matches found)"), value=data__definemacros_item__1, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}][1]".format(**locals()) + "", definition={'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}, rule='oneOf')
if data__definemacros_item_len > 2:
raise JsonSchemaValueException("" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + " must contain only specified items", value=data__definemacros_item, name="" + (name_prefix or "data") + ".define-macros[{data__definemacros_x}]".format(**locals()) + "", definition={'type': 'array', 'items': [{'description': 'macro name', 'type': 'string'}, {'description': 'macro value', 'oneOf': [{'type': 'string'}, {'type': 'null'}]}], 'additionalItems': False}, rule='items')
if "undef-macros" in data_keys:
@@ -889,26 +941,26 @@ def validate_https___setuptools_pypa_io_en_latest_userguide_pyproject_config_htm
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': [{'type': 'string', 'format': 'python-module-name-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='type')
- data_any_of_count10 = 0
- if not data_any_of_count10:
+ data_any_of_count11 = 0
+ if not data_any_of_count11:
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-relaxed'}, rule='type')
if isinstance(data, str):
if not custom_formats["python-module-name-relaxed"](data):
raise JsonSchemaValueException("" + (name_prefix or "data") + " must be python-module-name-relaxed", value=data, name="" + (name_prefix or "data") + "", definition={'type': 'string', 'format': 'python-module-name-relaxed'}, rule='format')
- data_any_of_count10 += 1
+ data_any_of_count11 += 1
except JsonSchemaValueException: pass
- if not data_any_of_count10:
+ if not data_any_of_count11:
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={'type': 'string', 'format': 'pep561-stub-name'}, rule='format')
- data_any_of_count10 += 1
+ data_any_of_count11 += 1
except JsonSchemaValueException: pass
- if not data_any_of_count10:
+ if not data_any_of_count11:
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-relaxed'}, {'type': 'string', 'format': 'pep561-stub-name'}]}, rule='anyOf')
return data
@@ -933,12 +985,63 @@ def validate_https___setuptools_pypa_io_en_latest_deprecated_distutils_configfil
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/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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'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']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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')
+ try:
+ try:
+ data_is_dict = isinstance(data, dict)
+ if data_is_dict:
+ 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")
+ data__dynamic = data["dynamic"]
+ data__dynamic_is_list = isinstance(data__dynamic, (list, tuple))
+ if data__dynamic_is_list:
+ data__dynamic_contains = False
+ for data__dynamic_key in data__dynamic:
+ try:
+ if data__dynamic_key != "version":
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be same as const definition: version", value=data__dynamic_key, name="" + (name_prefix or "data") + ".dynamic", definition={'const': 'version'}, rule='const')
+ data__dynamic_contains = True
+ break
+ except JsonSchemaValueException: pass
+ if not data__dynamic_contains:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must contain one of contains definition", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}, rule='contains')
+ except JsonSchemaValueException: pass
+ else:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + " must NOT match a disallowed definition", value=data, name="" + (name_prefix or "data") + "", definition={'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, rule='not')
+ except JsonSchemaValueException:
+ pass
+ else:
+ data_is_dict = isinstance(data, dict)
+ if data_is_dict:
+ data__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')
+ try:
+ data_is_dict = isinstance(data, dict)
+ if data_is_dict:
+ data__missing_keys = set(['license-files']) - 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': ['license-files']}, rule='required')
+ except JsonSchemaValueException:
+ pass
+ else:
+ data_is_dict = isinstance(data, dict)
+ if data_is_dict:
+ data_keys = set(data.keys())
+ if "license" in data_keys:
+ data_keys.remove("license")
+ data__license = data["license"]
+ if not isinstance(data__license, (str)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be string", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'string'}, rule='type')
data_is_dict = isinstance(data, dict)
if data_is_dict:
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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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')
+ 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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'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']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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")
@@ -964,19 +1067,19 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom
if "readme" in data_keys:
data_keys.remove("readme")
data__readme = data["readme"]
- data__readme_one_of_count11 = 0
- if data__readme_one_of_count11 < 2:
+ data__readme_one_of_count12 = 0
+ if data__readme_one_of_count12 < 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_count11 += 1
+ data__readme_one_of_count12 += 1
except JsonSchemaValueException: pass
- if data__readme_one_of_count11 < 2:
+ if data__readme_one_of_count12 < 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_count12 = 0
- if not data__readme_any_of_count12:
+ data__readme_any_of_count13 = 0
+ if not data__readme_any_of_count13:
try:
data__readme_is_dict = isinstance(data__readme, dict)
if data__readme_is_dict:
@@ -989,9 +1092,9 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom
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_count12 += 1
+ data__readme_any_of_count13 += 1
except JsonSchemaValueException: pass
- if not data__readme_any_of_count12:
+ if not data__readme_any_of_count13:
try:
data__readme_is_dict = isinstance(data__readme, dict)
if data__readme_is_dict:
@@ -1004,9 +1107,9 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom
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_count12 += 1
+ data__readme_any_of_count13 += 1
except JsonSchemaValueException: pass
- if not data__readme_any_of_count12:
+ if not data__readme_any_of_count13:
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:
@@ -1019,10 +1122,10 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom
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_count11 += 1
+ data__readme_one_of_count12 += 1
except JsonSchemaValueException: pass
- if data__readme_one_of_count11 != 1:
- raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count11) + " 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', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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_count12 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".readme must be valid exactly by one definition" + (" (" + str(data__readme_one_of_count12) + " 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', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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"]
@@ -1034,39 +1137,63 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom
if "license" in data_keys:
data_keys.remove("license")
data__license = data["license"]
- data__license_one_of_count13 = 0
- if data__license_one_of_count13 < 2:
+ data__license_one_of_count14 = 0
+ if data__license_one_of_count14 < 2:
+ try:
+ if not isinstance(data__license, (str)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be string", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, rule='type')
+ if isinstance(data__license, str):
+ if not custom_formats["SPDX"](data__license):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be SPDX", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, rule='format')
+ data__license_one_of_count14 += 1
+ except JsonSchemaValueException: pass
+ if data__license_one_of_count14 < 2:
try:
+ if not isinstance(data__license, (dict)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be object", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, rule='type')
data__license_is_dict = isinstance(data__license, dict)
if data__license_is_dict:
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')
+ 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={'type': 'object', '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_count13 += 1
+ data__license_one_of_count14 += 1
except JsonSchemaValueException: pass
- if data__license_one_of_count13 < 2:
+ if data__license_one_of_count14 < 2:
try:
+ if not isinstance(data__license, (dict)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be object", value=data__license, name="" + (name_prefix or "data") + ".license", definition={'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}, rule='type')
data__license_is_dict = isinstance(data__license, dict)
if data__license_is_dict:
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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}, rule='required')
+ 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={'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}, rule='type')
- data__license_one_of_count13 += 1
+ data__license_one_of_count14 += 1
except JsonSchemaValueException: pass
- if data__license_one_of_count13 != 1:
- raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count13) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, rule='oneOf')
+ if data__license_one_of_count14 != 1:
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license must be valid exactly by one definition" + (" (" + str(data__license_one_of_count14) + " matches found)"), value=data__license, name="" + (name_prefix or "data") + ".license", definition={'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, rule='oneOf')
+ if "license-files" in data_keys:
+ data_keys.remove("license-files")
+ data__licensefiles = data["license-files"]
+ if not isinstance(data__licensefiles, (list, tuple)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files must be array", value=data__licensefiles, name="" + (name_prefix or "data") + ".license-files", definition={'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, rule='type')
+ data__licensefiles_is_list = isinstance(data__licensefiles, (list, tuple))
+ if data__licensefiles_is_list:
+ data__licensefiles_len = len(data__licensefiles)
+ for data__licensefiles_x, data__licensefiles_item in enumerate(data__licensefiles):
+ if not isinstance(data__licensefiles_item, (str)):
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + " must be string", value=data__licensefiles_item, name="" + (name_prefix or "data") + ".license-files[{data__licensefiles_x}]".format(**locals()) + "", definition={'type': 'string'}, rule='type')
if "authors" in data_keys:
data_keys.remove("authors")
data__authors = data["authors"]
@@ -1211,49 +1338,15 @@ def validate_https___packaging_python_org_en_latest_specifications_pyproject_tom
data_keys.remove("dynamic")
data__dynamic = data["dynamic"]
if not isinstance(data__dynamic, (list, tuple)):
- raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be array", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}, rule='type')
+ raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be array", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'type': 'array', '$$description': ['Specifies which fields are intentionally unspecified and expected to be', 'dynamically provided by build tools'], 'items': {'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}, rule='type')
data__dynamic_is_list = isinstance(data__dynamic, (list, tuple))
if data__dynamic_is_list:
data__dynamic_len = len(data__dynamic)
for data__dynamic_x, data__dynamic_item in enumerate(data__dynamic):
- if data__dynamic_item not in ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']:
- raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + " must be one of ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']", value=data__dynamic_item, name="" + (name_prefix or "data") + ".dynamic[{data__dynamic_x}]".format(**locals()) + "", definition={'enum': ['version', 'description', 'readme', 'requires-python', 'license', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}, rule='enum')
+ if data__dynamic_item not in ['version', 'description', 'readme', 'requires-python', 'license', 'license-files', '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', 'license-files', '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', 'license-files', '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/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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#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', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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__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")
- data__dynamic = data["dynamic"]
- data__dynamic_is_list = isinstance(data__dynamic, (list, tuple))
- if data__dynamic_is_list:
- data__dynamic_contains = False
- for data__dynamic_key in data__dynamic:
- try:
- if data__dynamic_key != "version":
- raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must be same as const definition: version", value=data__dynamic_key, name="" + (name_prefix or "data") + ".dynamic", definition={'const': 'version'}, rule='const')
- data__dynamic_contains = True
- break
- except JsonSchemaValueException: pass
- if not data__dynamic_contains:
- raise JsonSchemaValueException("" + (name_prefix or "data") + ".dynamic must contain one of contains definition", value=data__dynamic, name="" + (name_prefix or "data") + ".dynamic", definition={'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}, rule='contains')
- except JsonSchemaValueException: pass
- else:
- raise JsonSchemaValueException("" + (name_prefix or "data") + " must NOT match a disallowed definition", value=data, name="" + (name_prefix or "data") + "", definition={'not': {'required': ['dynamic'], 'properties': {'dynamic': {'contains': {'const': 'version'}, '$$description': ['version is listed in ``dynamic``']}}}, '$$comment': ['According to :pep:`621`:', ' If the core metadata specification lists a field as "Required", then', ' the metadata MUST specify the field statically or list it in dynamic', 'In turn, `core metadata`_ defines:', ' The required fields are: Metadata-Version, Name, Version.', ' All the other fields are optional.', 'Since ``Metadata-Version`` is defined by the build back-end, ``name`` and', '``version`` are the only mandatory information in ``pyproject.toml``.', '.. _core metadata: https://packaging.python.org/specifications/core-metadata/']}, rule='not')
- except JsonSchemaValueException:
- pass
- else:
- data_is_dict = isinstance(data, dict)
- if data_is_dict:
- data__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')
+ 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', '<https://packaging.python.org/specifications/core-metadata/#summary>`_']}, 'readme': {'$$description': ['`Full/detailed description of the project in the form of a README', '<https://peps.python.org/pep-0621/#readme>`_', "with meaning similar to the one defined in `core metadata's Description", '<https://packaging.python.org/specifications/core-metadata/#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', '<https://packaging.python.org/specifications/core-metadata/#requires-python>`_.']}, 'license': {'description': '`Project license <https://peps.python.org/pep-0621/#license>`_.', 'oneOf': [{'type': 'string', 'description': 'An SPDX license identifier', 'format': 'SPDX'}, {'type': 'object', 'properties': {'file': {'type': 'string', '$$description': ['Relative path to the file (UTF-8) which contains the license for the', 'project.']}}, 'required': ['file']}, {'type': 'object', 'properties': {'text': {'type': 'string', '$$description': ['The license of the project whose meaning is that of the', '`License field from the core metadata', '<https://packaging.python.org/specifications/core-metadata/#license>`_.']}}, 'required': ['text']}]}, 'license-files': {'description': 'Paths or globs to paths of license files', 'type': 'array', 'items': {'type': 'string'}}, '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 <https://pypi.org/classifiers/>`_.'}, '$$description': ['`Trove classifiers <https://pypi.org/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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', '<https://packaging.python.org/specifications/entry-points/>`_.'], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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', 'license-files', 'authors', 'maintainers', 'keywords', 'classifiers', 'urls', 'scripts', 'gui-scripts', 'entry-points', 'dependencies', 'optional-dependencies']}}}, 'required': ['name'], 'additionalProperties': False, 'allOf': [{'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']}}, {'if': {'required': ['license-files']}, 'then': {'properties': {'license': {'type': 'string'}}}}], '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', '<https://packaging.python.org/specifications/entry-points/>`_', 'and `setuptools docs', '<https://setuptools.pypa.io/en/latest/userguide/entry_point.html>`_', '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___packaging_python_org_en_latest_specifications_pyproject_toml___definitions_dependency(data, custom_formats={}, name_prefix=None):
diff --git a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py
index 153b1f0b27f..1cf4a465ef0 100644
--- a/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py
+++ b/contrib/python/setuptools/py3/setuptools/config/_validate_pyproject/formats.py
@@ -164,12 +164,15 @@ class _TroveClassifier:
"""
downloaded: typing.Union[None, "Literal[False]", typing.Set[str]]
+ """
+ None => not cached yet
+ False => unavailable
+ set => cached values
+ """
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) -> None:
@@ -351,7 +354,7 @@ def python_entrypoint_reference(value: str) -> bool:
obj = rest
module_parts = module.split(".")
- identifiers = _chain(module_parts, obj.split(".")) if rest else module_parts
+ identifiers = _chain(module_parts, obj.split(".")) if rest else iter(module_parts)
return all(python_identifier(i.strip()) for i in identifiers)
@@ -373,3 +376,27 @@ def uint(value: builtins.int) -> bool:
def int(value: builtins.int) -> bool:
r"""Signed 64-bit integer (:math:`-2^{63} \leq x < 2^{63}`)"""
return -(2**63) <= value < 2**63
+
+
+try:
+ from packaging import licenses as _licenses
+
+ def SPDX(value: str) -> bool:
+ """See :ref:`PyPA's License-Expression specification
+ <pypa:core-metadata-license-expression>` (added in :pep:`639`).
+ """
+ try:
+ _licenses.canonicalize_license_expression(value)
+ return True
+ except _licenses.InvalidLicenseExpression:
+ return False
+
+except ImportError: # pragma: no cover
+ _logger.warning(
+ "Could not find an up-to-date installation of `packaging`. "
+ "License expressions might not be validated. "
+ "To enforce validation, please install `packaging>=24.2`."
+ )
+
+ def SPDX(value: str) -> bool:
+ return True
diff --git a/contrib/python/setuptools/py3/setuptools/dist.py b/contrib/python/setuptools/py3/setuptools/dist.py
index 02496512672..8d972cc49bd 100644
--- a/contrib/python/setuptools/py3/setuptools/dist.py
+++ b/contrib/python/setuptools/py3/setuptools/dist.py
@@ -1,13 +1,14 @@
from __future__ import annotations
+import functools
import io
import itertools
import numbers
import os
import re
import sys
-from collections.abc import Iterable, MutableMapping, Sequence
-from glob import iglob
+from collections.abc import Iterable, Iterator, MutableMapping, Sequence
+from glob import glob
from pathlib import Path
from typing import TYPE_CHECKING, Any, Union
@@ -23,10 +24,12 @@ from . import (
command as _, # noqa: F401 # imported for side-effects
)
from ._importlib import metadata
+from ._normalization import _canonicalize_license_expression
from ._path import StrPath
from ._reqs import _StrOrIter
from .config import pyprojecttoml, setupcfg
from .discovery import ConfigDiscovery
+from .errors import InvalidConfigError
from .monkey import get_unpatched
from .warnings import InformationOnly, SetuptoolsDeprecationWarning
@@ -288,6 +291,7 @@ class Distribution(_Distribution):
'long_description_content_type': lambda: None,
'project_urls': dict,
'provides_extras': dict, # behaves like an ordered set
+ 'license_expression': lambda: None,
'license_file': lambda: None,
'license_files': lambda: None,
'install_requires': list,
@@ -402,6 +406,46 @@ class Distribution(_Distribution):
(k, list(map(str, _reqs.parse(v or [])))) for k, v in extras_require.items()
)
+ def _finalize_license_expression(self) -> None:
+ """
+ Normalize license and license_expression.
+ >>> dist = Distribution({"license_expression": _static.Str("mit aNd gpl-3.0-OR-later")})
+ >>> _static.is_static(dist.metadata.license_expression)
+ True
+ >>> dist._finalize_license_expression()
+ >>> _static.is_static(dist.metadata.license_expression) # preserve "static-ness"
+ True
+ >>> print(dist.metadata.license_expression)
+ MIT AND GPL-3.0-or-later
+ """
+ classifiers = self.metadata.get_classifiers()
+ license_classifiers = [cl for cl in classifiers if cl.startswith("License :: ")]
+
+ license_expr = self.metadata.license_expression
+ if license_expr:
+ str_ = _static.Str if _static.is_static(license_expr) else str
+ normalized = str_(_canonicalize_license_expression(license_expr))
+ if license_expr != normalized:
+ InformationOnly.emit(f"Normalizing '{license_expr}' to '{normalized}'")
+ self.metadata.license_expression = normalized
+ if license_classifiers:
+ raise InvalidConfigError(
+ "License classifiers have been superseded by license expressions "
+ "(see https://peps.python.org/pep-0639/). Please remove:\n\n"
+ + "\n".join(license_classifiers),
+ )
+ elif license_classifiers:
+ pypa_guides = "guides/writing-pyproject-toml/#license"
+ SetuptoolsDeprecationWarning.emit(
+ "License classifiers are deprecated.",
+ "Please consider removing the following classifiers in favor of a "
+ "SPDX license expression:\n\n" + "\n".join(license_classifiers),
+ see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+ # Warning introduced on 2025-02-17
+ # TODO: Should we add a due date? It may affect old/unmaintained
+ # packages in the ecosystem and cause problems...
+ )
+
def _finalize_license_files(self) -> None:
"""Compute names of all license files which should be included."""
license_files: list[str] | None = self.metadata.license_files
@@ -416,26 +460,88 @@ class Distribution(_Distribution):
# See https://wheel.readthedocs.io/en/stable/user_guide.html
# -> 'Including license files in the generated wheel file'
patterns = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']
+ files = self._expand_patterns(patterns, enforce_match=False)
+ else: # Patterns explicitly given by the user
+ files = self._expand_patterns(patterns, enforce_match=True)
- self.metadata.license_files = list(
- unique_everseen(self._expand_patterns(patterns))
- )
+ self.metadata.license_files = list(unique_everseen(files))
- @staticmethod
- def _expand_patterns(patterns):
+ @classmethod
+ def _expand_patterns(
+ cls, patterns: list[str], enforce_match: bool = True
+ ) -> Iterator[str]:
"""
>>> list(Distribution._expand_patterns(['LICENSE']))
['LICENSE']
>>> list(Distribution._expand_patterns(['pyproject.toml', 'LIC*']))
['pyproject.toml', 'LICENSE']
+ >>> list(Distribution._expand_patterns(['setuptools/**/pyprojecttoml.py']))
+ ['setuptools/config/pyprojecttoml.py']
"""
return (
- path
+ path.replace(os.sep, "/")
for pattern in patterns
- for path in sorted(iglob(pattern))
+ for path in sorted(cls._find_pattern(pattern, enforce_match))
if not path.endswith('~') and os.path.isfile(path)
)
+ @staticmethod
+ def _find_pattern(pattern: str, enforce_match: bool = True) -> list[str]:
+ r"""
+ >>> Distribution._find_pattern("LICENSE")
+ ['LICENSE']
+ >>> Distribution._find_pattern("/LICENSE.MIT")
+ Traceback (most recent call last):
+ ...
+ setuptools.errors.InvalidConfigError: Pattern '/LICENSE.MIT' should be relative...
+ >>> Distribution._find_pattern("../LICENSE.MIT")
+ Traceback (most recent call last):
+ ...
+ setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern '../LICENSE.MIT' cannot contain '..'...
+ >>> Distribution._find_pattern("LICEN{CSE*")
+ Traceback (most recent call last):
+ ...
+ setuptools.warnings.SetuptoolsDeprecationWarning: ...Pattern 'LICEN{CSE*' contains invalid characters...
+ """
+ pypa_guides = "specifications/glob-patterns/"
+ if ".." in pattern:
+ SetuptoolsDeprecationWarning.emit(
+ f"Pattern {pattern!r} cannot contain '..'",
+ """
+ Please ensure the files specified are contained by the root
+ of the Python package (normally marked by `pyproject.toml`).
+ """,
+ see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+ due_date=(2026, 3, 20), # Introduced in 2025-03-20
+ # Replace with InvalidConfigError after deprecation
+ )
+ if pattern.startswith((os.sep, "/")) or ":\\" in pattern:
+ raise InvalidConfigError(
+ f"Pattern {pattern!r} should be relative and must not start with '/'"
+ )
+ if re.match(r'^[\w\-\.\/\*\?\[\]]+$', pattern) is None:
+ SetuptoolsDeprecationWarning.emit(
+ "Please provide a valid glob pattern.",
+ "Pattern {pattern!r} contains invalid characters.",
+ pattern=pattern,
+ see_url=f"https://packaging.python.org/en/latest/{pypa_guides}",
+ due_date=(2026, 3, 20), # Introduced in 2025-02-20
+ )
+
+ found = glob(pattern, recursive=True)
+
+ if enforce_match and not found:
+ SetuptoolsDeprecationWarning.emit(
+ "Cannot find any files for the given pattern.",
+ "Pattern {pattern!r} did not match any files.",
+ pattern=pattern,
+ due_date=(2026, 3, 20), # Introduced in 2025-02-20
+ # PEP 639 requires us to error, but as a transition period
+ # we will only issue a warning to give people time to prepare.
+ # After the transition, this should raise an InvalidConfigError.
+ )
+ return found
+
# FIXME: 'Distribution._parse_config_files' is too complex (14)
def _parse_config_files(self, filenames=None): # noqa: C901
"""
@@ -490,8 +596,8 @@ class Distribution(_Distribution):
continue
val = parser.get(section, opt)
- opt = self.warn_dash_deprecation(opt, section)
- opt = self.make_option_lowercase(opt, section)
+ opt = self._enforce_underscore(opt, section)
+ opt = self._enforce_option_lowercase(opt, section)
opt_dict[opt] = (filename, val)
# Make the ConfigParser forget everything (so we retain
@@ -516,65 +622,63 @@ class Distribution(_Distribution):
except ValueError as e:
raise DistutilsOptionError(e) from e
- def warn_dash_deprecation(self, opt: str, section: str) -> str:
- if section in (
- 'options.extras_require',
- 'options.data_files',
- ):
+ def _enforce_underscore(self, opt: str, section: str) -> str:
+ if "-" not in opt or self._skip_setupcfg_normalization(section):
return opt
underscore_opt = opt.replace('-', '_')
- commands = list(
- itertools.chain(
- distutils.command.__all__,
- self._setuptools_commands(),
- )
+ affected = f"(Affected: {self.metadata.name})." if self.metadata.name else ""
+ SetuptoolsDeprecationWarning.emit(
+ f"Invalid dash-separated key {opt!r} in {section!r} (setup.cfg), "
+ f"please use the underscore name {underscore_opt!r} instead.",
+ f"""
+ Usage of dash-separated {opt!r} will not be supported in future
+ versions. Please use the underscore name {underscore_opt!r} instead.
+ {affected}
+ """,
+ see_docs="userguide/declarative_config.html",
+ due_date=(2026, 3, 3),
+ # Warning initially introduced in 3 Mar 2021
)
- if (
- not section.startswith('options')
- and section != 'metadata'
- and section not in commands
- ):
- return underscore_opt
-
- if '-' in opt:
- SetuptoolsDeprecationWarning.emit(
- "Invalid dash-separated options",
- f"""
- Usage of dash-separated {opt!r} will not be supported in future
- versions. Please use the underscore name {underscore_opt!r} instead.
- """,
- see_docs="userguide/declarative_config.html",
- due_date=(2025, 3, 3),
- # Warning initially introduced in 3 Mar 2021
- )
return underscore_opt
- def _setuptools_commands(self):
- try:
- 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 []
-
- def make_option_lowercase(self, opt: str, section: str) -> str:
- if section != 'metadata' or opt.islower():
+ def _enforce_option_lowercase(self, opt: str, section: str) -> str:
+ if opt.islower() or self._skip_setupcfg_normalization(section):
return opt
lowercase_opt = opt.lower()
+ affected = f"(Affected: {self.metadata.name})." if self.metadata.name else ""
SetuptoolsDeprecationWarning.emit(
- "Invalid uppercase configuration",
+ f"Invalid uppercase key {opt!r} in {section!r} (setup.cfg), "
+ f"please use lowercase {lowercase_opt!r} instead.",
f"""
Usage of uppercase key {opt!r} in {section!r} will not be supported in
future versions. Please use lowercase {lowercase_opt!r} instead.
+ {affected}
""",
see_docs="userguide/declarative_config.html",
- due_date=(2025, 3, 3),
+ due_date=(2026, 3, 3),
# Warning initially introduced in 6 Mar 2021
)
return lowercase_opt
+ def _skip_setupcfg_normalization(self, section: str) -> bool:
+ skip = (
+ 'options.extras_require',
+ 'options.data_files',
+ 'options.entry_points',
+ 'options.package_data',
+ 'options.exclude_package_data',
+ )
+ return section in skip or not self._is_setuptools_section(section)
+
+ def _is_setuptools_section(self, section: str) -> bool:
+ return (
+ section == "metadata"
+ or section.startswith("options")
+ or section in _setuptools_commands()
+ )
+
# FIXME: 'Distribution._set_command_options' is too complex (14)
def _set_command_options(self, command_obj, option_dict=None): # noqa: C901
"""
@@ -652,6 +756,7 @@ class Distribution(_Distribution):
pyprojecttoml.apply_configuration(self, filename, ignore_option_errors)
self._finalize_requires()
+ self._finalize_license_expression()
self._finalize_license_files()
def fetch_build_eggs(
@@ -999,6 +1104,18 @@ class Distribution(_Distribution):
super().run_command(command)
+def _setuptools_commands() -> set[str]:
+ try:
+ # Use older API for importlib.metadata compatibility
+ entry_points = metadata.distribution('setuptools').entry_points
+ eps: Iterable[str] = (ep.name for ep in entry_points)
+ except metadata.PackageNotFoundError:
+ # during bootstrapping, distribution doesn't exist
+ eps = []
+ return {*distutils.command.__all__, *eps}
+
+
class DistDeprecationWarning(SetuptoolsDeprecationWarning):
"""Class for warning about deprecations in dist in
setuptools. Not ignored by default, unlike DeprecationWarning."""
diff --git a/contrib/python/setuptools/py3/setuptools/msvc.py b/contrib/python/setuptools/py3/setuptools/msvc.py
index 9c9a63568ef..313a781ae09 100644
--- a/contrib/python/setuptools/py3/setuptools/msvc.py
+++ b/contrib/python/setuptools/py3/setuptools/msvc.py
@@ -1024,6 +1024,16 @@ class EnvironmentInfo:
------
list of str
paths
+
+ When host CPU is ARM, the tools should be found for ARM.
+
+ >>> getfixture('windows_only')
+ >>> mp = getfixture('monkeypatch')
+ >>> mp.setattr(PlatformInfo, 'current_cpu', 'arm64')
+ >>> ei = EnvironmentInfo(arch='irrelevant')
+ >>> paths = ei.VCTools
+ >>> any('HostARM64' in path for path in paths)
+ True
"""
si = self.si
tools = [os.path.join(si.VCInstallDir, 'VCPackages')]
@@ -1038,9 +1048,8 @@ class EnvironmentInfo:
tools += [os.path.join(si.VCInstallDir, path)]
elif self.vs_ver >= 15.0:
- host_dir = (
- r'bin\HostX86%s' if self.pi.current_is_x86() else r'bin\HostX64%s'
- )
+ host_id = self.pi.current_cpu.replace('amd64', 'x64').upper()
+ host_dir = os.path.join('bin', f'Host{host_id}%s')
tools += [
os.path.join(si.VCInstallDir, host_dir % self.pi.target_dir(x64=True))
]
diff --git a/contrib/python/setuptools/py3/setuptools/package_index.py b/contrib/python/setuptools/py3/setuptools/package_index.py
index 1a6abebcda4..3500c2d86f1 100644
--- a/contrib/python/setuptools/py3/setuptools/package_index.py
+++ b/contrib/python/setuptools/py3/setuptools/package_index.py
@@ -807,21 +807,63 @@ class PackageIndex(Environment):
else:
raise DistutilsError(f"Download error for {url}: {v}") from v
- def _download_url(self, url, tmpdir):
- # Determine download filename
- #
+ @staticmethod
+ def _sanitize(name):
+ r"""
+ Replace unsafe path directives with underscores.
+
+ >>> san = PackageIndex._sanitize
+ >>> san('/home/user/.ssh/authorized_keys')
+ '_home_user_.ssh_authorized_keys'
+ >>> san('..\\foo\\bing')
+ '__foo_bing'
+ >>> san('D:bar')
+ 'D_bar'
+ >>> san('C:\\bar')
+ 'C__bar'
+ >>> san('foo..bar')
+ 'foo..bar'
+ >>> san('D:../foo')
+ 'D___foo'
+ """
+ pattern = '|'.join((
+ # drive letters
+ r':',
+ # path separators
+ r'[/\\]',
+ # parent dirs
+ r'(?:(?<=([/\\]|:))\.\.(?=[/\\]|$))|(?:^\.\.(?=[/\\]|$))',
+ ))
+ return re.sub(pattern, r'_', name)
+
+ @classmethod
+ def _resolve_download_filename(cls, url, tmpdir):
+ """
+ >>> import pathlib
+ >>> du = PackageIndex._resolve_download_filename
+ >>> root = getfixture('tmp_path')
+ >>> url = 'https://files.pythonhosted.org/packages/a9/5a/0db.../setuptools-78.1.0.tar.gz'
+ >>> str(pathlib.Path(du(url, root)).relative_to(root))
+ 'setuptools-78.1.0.tar.gz'
+ """
name, _fragment = egg_info_for_url(url)
- if name:
- while '..' in name:
- name = name.replace('..', '.').replace('\\', '_')
- else:
- name = "__downloaded__" # default if URL has no path contents
+ name = cls._sanitize(
+ name
+ or
+ # default if URL has no path contents
+ '__downloaded__'
+ )
- if name.endswith('.egg.zip'):
- name = name[:-4] # strip the extra .zip before download
+ # strip any extra .zip before download
+ name = re.sub(r'\.egg\.zip$', '.egg', name)
- filename = os.path.join(tmpdir, name)
+ return os.path.join(tmpdir, name)
+ def _download_url(self, url, tmpdir):
+ """
+ Determine the download filename.
+ """
+ filename = self._resolve_download_filename(url, tmpdir)
return self._download_vcs(url, filename) or self._download_other(url, filename)
@staticmethod
diff --git a/contrib/python/setuptools/py3/setuptools/unicode_utils.py b/contrib/python/setuptools/py3/setuptools/unicode_utils.py
index a6e33f2e0d2..f502f5b0896 100644
--- a/contrib/python/setuptools/py3/setuptools/unicode_utils.py
+++ b/contrib/python/setuptools/py3/setuptools/unicode_utils.py
@@ -86,7 +86,7 @@ class _Utf8EncodingNeeded(SetuptoolsDeprecationWarning):
"""
_DETAILS = """
- Fallback behaviour for UTF-8 is considered **deprecated** and future versions of
+ Fallback behavior 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.
diff --git a/contrib/python/setuptools/py3/ya.make b/contrib/python/setuptools/py3/ya.make
index d28ff13d742..e36854d6165 100644
--- a/contrib/python/setuptools/py3/ya.make
+++ b/contrib/python/setuptools/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(75.8.2)
+VERSION(79.0.1)
LICENSE(MIT)
@@ -64,7 +64,16 @@ PY_SRCS(
setuptools/_distutils/command/install_scripts.py
setuptools/_distutils/command/sdist.py
setuptools/_distutils/compat/__init__.py
+ setuptools/_distutils/compat/numpy.py
setuptools/_distutils/compat/py39.py
+ setuptools/_distutils/compilers/C/__init__.py
+ setuptools/_distutils/compilers/C/base.py
+ setuptools/_distutils/compilers/C/cygwin.py
+ setuptools/_distutils/compilers/C/errors.py
+ setuptools/_distutils/compilers/C/msvc.py
+ setuptools/_distutils/compilers/C/unix.py
+ setuptools/_distutils/compilers/C/zos.py
+ setuptools/_distutils/compilers/__init__.py
setuptools/_distutils/core.py
setuptools/_distutils/cygwinccompiler.py
setuptools/_distutils/debug.py