aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2025-01-24 00:01:17 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2025-01-24 00:13:36 +0300
commite0834724754ae9e26fd14e27027e69cc22d1939f (patch)
treef2d16bc64fc4307f957913b1050a2916fafdf1b9
parent707a3047759771809e881e169d40f37de19b8a7c (diff)
downloadydb-e0834724754ae9e26fd14e27027e69cc22d1939f.tar.gz
Intermediate changes
commit_hash:e49cc600846762b0cef5d379812c52e8cb8f52f1
-rw-r--r--contrib/python/setuptools/py3/.dist-info/METADATA4
-rw-r--r--contrib/python/setuptools/py3/setuptools/_core_metadata.py37
-rw-r--r--contrib/python/setuptools/py3/setuptools/_static.py188
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py76
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/expand.py5
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/setupcfg.py52
-rw-r--r--contrib/python/setuptools/py3/setuptools/dist.py14
-rw-r--r--contrib/python/setuptools/py3/ya.make3
8 files changed, 323 insertions, 56 deletions
diff --git a/contrib/python/setuptools/py3/.dist-info/METADATA b/contrib/python/setuptools/py3/.dist-info/METADATA
index 96aa363053..0c941771c5 100644
--- a/contrib/python/setuptools/py3/.dist-info/METADATA
+++ b/contrib/python/setuptools/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
-Metadata-Version: 2.1
+Metadata-Version: 2.2
Name: setuptools
-Version: 75.7.0
+Version: 75.8.0
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Author-email: Python Packaging Authority <distutils-sig@python.org>
Project-URL: Source, https://github.com/pypa/setuptools
diff --git a/contrib/python/setuptools/py3/setuptools/_core_metadata.py b/contrib/python/setuptools/py3/setuptools/_core_metadata.py
index 642b80df31..850cc409f7 100644
--- a/contrib/python/setuptools/py3/setuptools/_core_metadata.py
+++ b/contrib/python/setuptools/py3/setuptools/_core_metadata.py
@@ -19,6 +19,7 @@ from packaging.utils import canonicalize_name, canonicalize_version
from packaging.version import Version
from . import _normalization, _reqs
+from ._static import is_static
from .warnings import SetuptoolsDeprecationWarning
from distutils.util import rfc822_escape
@@ -27,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.1')
+ mv = Version('2.2')
self.metadata_version = mv
return mv
@@ -207,6 +208,10 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME
self._write_list(file, 'License-File', self.license_files or [])
_write_requirements(self, file)
+ for field, attr in _POSSIBLE_DYNAMIC_FIELDS.items():
+ if (val := getattr(self, attr, None)) and not is_static(val):
+ write_field('Dynamic', field)
+
long_description = self.get_long_description()
if long_description:
file.write(f"\n{long_description}")
@@ -284,3 +289,33 @@ def _distribution_fullname(name: str, version: str) -> str:
canonicalize_name(name).replace('-', '_'),
canonicalize_version(version, strip_trailing_zero=False),
)
+
+
+_POSSIBLE_DYNAMIC_FIELDS = {
+ # Core Metadata Field x related Distribution attribute
+ "author": "author",
+ "author-email": "author_email",
+ "classifier": "classifiers",
+ "description": "long_description",
+ "description-content-type": "long_description_content_type",
+ "download-url": "download_url",
+ "home-page": "url",
+ "keywords": "keywords",
+ "license": "license",
+ # "license-file": "license_files", # XXX: does PEP 639 exempt Dynamic ??
+ "maintainer": "maintainer",
+ "maintainer-email": "maintainer_email",
+ "obsoletes": "obsoletes",
+ # "obsoletes-dist": "obsoletes_dist", # NOT USED
+ "platform": "platforms",
+ "project-url": "project_urls",
+ "provides": "provides",
+ # "provides-dist": "provides_dist", # NOT USED
+ "provides-extra": "extras_require",
+ "requires": "requires",
+ "requires-dist": "install_requires",
+ # "requires-external": "requires_external", # NOT USED
+ "requires-python": "python_requires",
+ "summary": "description",
+ # "supported-platform": "supported_platforms", # NOT USED
+}
diff --git a/contrib/python/setuptools/py3/setuptools/_static.py b/contrib/python/setuptools/py3/setuptools/_static.py
new file mode 100644
index 0000000000..075a0bcddf
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_static.py
@@ -0,0 +1,188 @@
+from functools import wraps
+from typing import TypeVar
+
+import packaging.specifiers
+
+from .warnings import SetuptoolsDeprecationWarning
+
+
+class Static:
+ """
+ Wrapper for built-in object types that are allow setuptools to identify
+ static core metadata (in opposition to ``Dynamic``, as defined :pep:`643`).
+
+ The trick is to mark values with :class:`Static` when they come from
+ ``pyproject.toml`` or ``setup.cfg``, so if any plugin overwrite the value
+ with a built-in, setuptools will be able to recognise the change.
+
+ We inherit from built-in classes, so that we don't need to change the existing
+ code base to deal with the new types.
+ We also should strive for immutability objects to avoid changes after the
+ initial parsing.
+ """
+
+ _mutated_: bool = False # TODO: Remove after deprecation warning is solved
+
+
+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
+ parsed from config files.
+ But we can attempt to block "in-place" mutations and identify when they
+ were done.
+ """
+ fn = getattr(target, method, None)
+ if fn is None:
+ return
+
+ @wraps(fn)
+ def _replacement(self: Static, *args, **kwargs):
+ # TODO: After deprecation period raise NotImplementedError instead of warning
+ # which obviated the existence and checks of the `_mutated_` attribute.
+ self._mutated_ = True
+ SetuptoolsDeprecationWarning.emit(
+ "Direct modification of value will be disallowed",
+ f"""
+ In an effort to implement PEP 643, direct/in-place changes of static values
+ that come from configuration files are deprecated.
+ If you need to modify this value, please first create a copy with {copying}
+ and make sure conform to all relevant standards when overriding setuptools
+ functionality (https://packaging.python.org/en/latest/specifications/).
+ """,
+ due_date=(2025, 10, 10), # Initially introduced in 2024-09-06
+ )
+ return fn(self, *args, **kwargs)
+
+ _replacement.__doc__ = "" # otherwise doctest may fail.
+ setattr(target, method, _replacement)
+
+
+class Str(str, Static):
+ pass
+
+
+class Tuple(tuple, Static):
+ pass
+
+
+class List(list, Static):
+ """
+ :meta private:
+ >>> x = List([1, 2, 3])
+ >>> is_static(x)
+ True
+ >>> x += [0] # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ SetuptoolsDeprecationWarning: Direct modification ...
+ >>> is_static(x) # no longer static after modification
+ False
+ >>> y = list(x)
+ >>> y.clear()
+ >>> y
+ []
+ >>> y == x
+ False
+ >>> is_static(List(y))
+ True
+ """
+
+
+# Make `List` immutable-ish
+# (certain places of setuptools/distutils issue a warn if we use tuple instead of list)
+for _method in (
+ '__delitem__',
+ '__iadd__',
+ '__setitem__',
+ 'append',
+ 'clear',
+ 'extend',
+ 'insert',
+ 'remove',
+ 'reverse',
+ 'pop',
+):
+ _prevent_modification(List, _method, "`list(value)`")
+
+
+class Dict(dict, Static):
+ """
+ :meta private:
+ >>> x = Dict({'a': 1, 'b': 2})
+ >>> is_static(x)
+ True
+ >>> x['c'] = 0 # doctest: +IGNORE_EXCEPTION_DETAIL
+ Traceback (most recent call last):
+ SetuptoolsDeprecationWarning: Direct modification ...
+ >>> x._mutated_
+ True
+ >>> is_static(x) # no longer static after modification
+ False
+ >>> y = dict(x)
+ >>> y.popitem()
+ ('b', 2)
+ >>> y == x
+ False
+ >>> is_static(Dict(y))
+ True
+ """
+
+
+# Make `Dict` immutable-ish (we cannot inherit from types.MappingProxyType):
+for _method in (
+ '__delitem__',
+ '__ior__',
+ '__setitem__',
+ 'clear',
+ 'pop',
+ 'popitem',
+ 'setdefault',
+ 'update',
+):
+ _prevent_modification(Dict, _method, "`dict(value)`")
+
+
+class SpecifierSet(packaging.specifiers.SpecifierSet, Static):
+ """Not exactly a built-in type but useful for ``requires-python``"""
+
+
+T = TypeVar("T")
+
+
+def noop(value: T) -> T:
+ """
+ >>> noop(42)
+ 42
+ """
+ return value
+
+
+_CONVERSIONS = {str: Str, tuple: Tuple, list: List, dict: Dict}
+
+
+def attempt_conversion(value: T) -> T:
+ """
+ >>> is_static(attempt_conversion("hello"))
+ True
+ >>> is_static(object())
+ False
+ """
+ return _CONVERSIONS.get(type(value), noop)(value) # type: ignore[call-overload]
+
+
+def is_static(value: object) -> bool:
+ """
+ >>> is_static(a := Dict({'a': 1}))
+ True
+ >>> is_static(dict(a))
+ False
+ >>> is_static(b := List([1, 2, 3]))
+ True
+ >>> is_static(list(b))
+ False
+ """
+ return isinstance(value, Static) and not value._mutated_
+
+
+EMPTY_LIST = List()
+EMPTY_DICT = Dict()
diff --git a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
index c4bbcff730..331596bdd7 100644
--- a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
+++ b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
@@ -20,6 +20,7 @@ from itertools import chain
from types import MappingProxyType
from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union
+from .. import _static
from .._path import StrPath
from ..errors import RemovedConfigError
from ..extension import Extension
@@ -65,10 +66,11 @@ def apply(dist: Distribution, config: dict, filename: StrPath) -> Distribution:
def _apply_project_table(dist: Distribution, config: dict, root_dir: StrPath):
- project_table = config.get("project", {}).copy()
- if not project_table:
+ orig_config = config.get("project", {})
+ if not orig_config:
return # short-circuit
+ project_table = {k: _static.attempt_conversion(v) for k, v in orig_config.items()}
_handle_missing_dynamic(dist, project_table)
_unify_entry_points(project_table)
@@ -98,7 +100,11 @@ def _apply_tool_table(dist: Distribution, config: dict, filename: StrPath):
raise RemovedConfigError("\n".join([cleandoc(msg), suggestion]))
norm_key = TOOL_TABLE_RENAMES.get(norm_key, norm_key)
- _set_config(dist, norm_key, value)
+ corresp = TOOL_TABLE_CORRESPONDENCE.get(norm_key, norm_key)
+ if callable(corresp):
+ corresp(dist, value)
+ else:
+ _set_config(dist, corresp, value)
_copy_command_options(config, dist, filename)
@@ -143,7 +149,7 @@ def _guess_content_type(file: str) -> str | None:
return None
if ext in _CONTENT_TYPES:
- return _CONTENT_TYPES[ext]
+ return _static.Str(_CONTENT_TYPES[ext])
valid = ", ".join(f"{k} ({v})" for k, v in _CONTENT_TYPES.items())
msg = f"only the following file extensions are recognized: {valid}."
@@ -165,10 +171,11 @@ def _long_description(
text = val.get("text") or expand.read_files(file, root_dir)
ctype = val["content-type"]
- _set_config(dist, "long_description", text)
+ # XXX: Is it completely safe to assume static?
+ _set_config(dist, "long_description", _static.Str(text))
if ctype:
- _set_config(dist, "long_description_content_type", ctype)
+ _set_config(dist, "long_description_content_type", _static.Str(ctype))
if file:
dist._referenced_files.add(file)
@@ -178,10 +185,12 @@ def _license(dist: Distribution, val: dict, root_dir: StrPath | None):
from setuptools.config import expand
if "file" in val:
- _set_config(dist, "license", expand.read_files([val["file"]], root_dir))
+ # 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", val["text"])
+ _set_config(dist, "license", _static.Str(val["text"]))
def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind: str):
@@ -197,9 +206,9 @@ def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind
email_field.append(str(addr))
if field:
- _set_config(dist, kind, ", ".join(field))
+ _set_config(dist, kind, _static.Str(", ".join(field)))
if email_field:
- _set_config(dist, f"{kind}_email", ", ".join(email_field))
+ _set_config(dist, f"{kind}_email", _static.Str(", ".join(email_field)))
def _project_urls(dist: Distribution, val: dict, _root_dir: StrPath | None):
@@ -207,9 +216,7 @@ def _project_urls(dist: Distribution, val: dict, _root_dir: StrPath | None):
def _python_requires(dist: Distribution, val: str, _root_dir: StrPath | None):
- from packaging.specifiers import SpecifierSet
-
- _set_config(dist, "python_requires", SpecifierSet(val))
+ _set_config(dist, "python_requires", _static.SpecifierSet(val))
def _dependencies(dist: Distribution, val: list, _root_dir: StrPath | None):
@@ -237,9 +244,14 @@ def _noop(_dist: Distribution, val: _T) -> _T:
return val
+def _identity(val: _T) -> _T:
+ return val
+
+
def _unify_entry_points(project_table: dict):
project = project_table
- entry_points = project.pop("entry-points", project.pop("entry_points", {}))
+ given = project.pop("entry-points", project.pop("entry_points", {}))
+ entry_points = dict(given) # Avoid problems with static
renaming = {"scripts": "console_scripts", "gui_scripts": "gui_scripts"}
for key, value in list(project.items()): # eager to allow modifications
norm_key = json_compatible_key(key)
@@ -333,6 +345,14 @@ def _get_previous_gui_scripts(dist: Distribution) -> list | None:
return value.get("gui_scripts")
+def _set_static_list_metadata(attr: str, dist: Distribution, val: list) -> None:
+ """Apply distutils metadata validation but preserve "static" behaviour"""
+ meta = dist.metadata
+ setter, getter = getattr(meta, f"set_{attr}"), getattr(meta, f"get_{attr}")
+ setter(val)
+ setattr(meta, attr, _static.List(getter()))
+
+
def _attrgetter(attr):
"""
Similar to ``operator.attrgetter`` but returns None if ``attr`` is not found
@@ -386,6 +406,12 @@ TOOL_TABLE_REMOVALS = {
See https://packaging.python.org/en/latest/guides/packaging-namespace-packages/.
""",
}
+TOOL_TABLE_CORRESPONDENCE = {
+ # Fields with corresponding core metadata need to be marked as static:
+ "obsoletes": partial(_set_static_list_metadata, "obsoletes"),
+ "provides": partial(_set_static_list_metadata, "provides"),
+ "platforms": partial(_set_static_list_metadata, "platforms"),
+}
SETUPTOOLS_PATCHES = {
"long_description_content_type",
@@ -422,17 +448,17 @@ _PREVIOUSLY_DEFINED = {
_RESET_PREVIOUSLY_DEFINED: dict = {
# Fix improper setting: given in `setup.py`, but not listed in `dynamic`
# dict: pyproject name => value to which reset
- "license": {},
- "authors": [],
- "maintainers": [],
- "keywords": [],
- "classifiers": [],
- "urls": {},
- "entry-points": {},
- "scripts": {},
- "gui-scripts": {},
- "dependencies": [],
- "optional-dependencies": {},
+ "license": _static.EMPTY_DICT,
+ "authors": _static.EMPTY_LIST,
+ "maintainers": _static.EMPTY_LIST,
+ "keywords": _static.EMPTY_LIST,
+ "classifiers": _static.EMPTY_LIST,
+ "urls": _static.EMPTY_DICT,
+ "entry-points": _static.EMPTY_DICT,
+ "scripts": _static.EMPTY_DICT,
+ "gui-scripts": _static.EMPTY_DICT,
+ "dependencies": _static.EMPTY_LIST,
+ "optional-dependencies": _static.EMPTY_DICT,
}
diff --git a/contrib/python/setuptools/py3/setuptools/config/expand.py b/contrib/python/setuptools/py3/setuptools/config/expand.py
index ccb5d63cd2..531f965013 100644
--- a/contrib/python/setuptools/py3/setuptools/config/expand.py
+++ b/contrib/python/setuptools/py3/setuptools/config/expand.py
@@ -34,6 +34,7 @@ from pathlib import Path
from types import ModuleType, TracebackType
from typing import TYPE_CHECKING, Any, Callable, TypeVar
+from .. import _static
from .._path import StrPath, same_path as _same_path
from ..discovery import find_package_path
from ..warnings import SetuptoolsWarning
@@ -181,7 +182,9 @@ def read_attr(
spec = _find_spec(module_name, path)
try:
- return getattr(StaticModule(module_name, spec), attr_name)
+ value = getattr(StaticModule(module_name, spec), attr_name)
+ # XXX: Is marking as static contents coming from modules too optimistic?
+ return _static.attempt_conversion(value)
except Exception:
# fallback to evaluate module
module = _load_spec(spec, module_name)
diff --git a/contrib/python/setuptools/py3/setuptools/config/setupcfg.py b/contrib/python/setuptools/py3/setuptools/config/setupcfg.py
index 4615815b6b..633aa9d45d 100644
--- a/contrib/python/setuptools/py3/setuptools/config/setupcfg.py
+++ b/contrib/python/setuptools/py3/setuptools/config/setupcfg.py
@@ -21,9 +21,9 @@ from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, TypeVar, cas
from packaging.markers import default_environment as marker_env
from packaging.requirements import InvalidRequirement, Requirement
-from packaging.specifiers import SpecifierSet
from packaging.version import InvalidVersion, Version
+from .. import _static
from .._path import StrPath
from ..errors import FileError, OptionError
from ..warnings import SetuptoolsDeprecationWarning
@@ -367,7 +367,7 @@ class ConfigHandler(Generic[Target]):
f'Only strings are accepted for the {key} field, '
'files are not accepted'
)
- return value
+ return _static.Str(value)
return parser
@@ -390,12 +390,13 @@ class ConfigHandler(Generic[Target]):
return value
if not value.startswith(include_directive):
- return value
+ return _static.Str(value)
spec = value[len(include_directive) :]
filepaths = [path.strip() for path in spec.split(',')]
self._referenced_files.update(filepaths)
- return expand.read_files(filepaths, root_dir)
+ # XXX: Is marking as static contents coming from files too optimistic?
+ return _static.Str(expand.read_files(filepaths, root_dir))
def _parse_attr(self, value, package_dir, root_dir: StrPath):
"""Represents value as a module attribute.
@@ -409,7 +410,7 @@ class ConfigHandler(Generic[Target]):
"""
attr_directive = 'attr:'
if not value.startswith(attr_directive):
- return value
+ return _static.Str(value)
attr_desc = value.replace(attr_directive, '')
@@ -548,23 +549,29 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
@property
def parsers(self):
"""Metadata item name to parser function mapping."""
- parse_list = self._parse_list
+ parse_list_static = self._get_parser_compound(self._parse_list, _static.List)
+ parse_dict_static = self._get_parser_compound(self._parse_dict, _static.Dict)
parse_file = partial(self._parse_file, root_dir=self.root_dir)
- parse_dict = self._parse_dict
exclude_files_parser = self._exclude_files_parser
return {
- 'platforms': parse_list,
- 'keywords': parse_list,
- 'provides': parse_list,
- 'obsoletes': parse_list,
- 'classifiers': self._get_parser_compound(parse_file, parse_list),
+ 'author': _static.Str,
+ 'author_email': _static.Str,
+ 'maintainer': _static.Str,
+ 'maintainer_email': _static.Str,
+ 'platforms': parse_list_static,
+ 'keywords': parse_list_static,
+ 'provides': parse_list_static,
+ 'obsoletes': parse_list_static,
+ 'classifiers': self._get_parser_compound(parse_file, parse_list_static),
'license': exclude_files_parser('license'),
- 'license_files': parse_list,
+ 'license_files': parse_list_static,
'description': parse_file,
'long_description': parse_file,
- 'version': self._parse_version,
- 'project_urls': parse_dict,
+ 'long_description_content_type': _static.Str,
+ 'version': self._parse_version, # Cannot be marked as dynamic
+ 'url': _static.Str,
+ 'project_urls': parse_dict_static,
}
def _parse_version(self, value):
@@ -620,20 +627,20 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
_warn_accidental_env_marker_misconfig(label, value, parsed)
# Filter it to only include lines that are not comments. `parse_list`
# will have stripped each line and filtered out empties.
- return [line for line in parsed if not line.startswith("#")]
+ return _static.List(line for line in parsed if not line.startswith("#"))
+ # ^-- Use `_static.List` to mark a non-`Dynamic` Core Metadata
@property
def parsers(self):
"""Metadata item name to parser function mapping."""
parse_list = self._parse_list
parse_bool = self._parse_bool
- parse_dict = self._parse_dict
parse_cmdclass = self._parse_cmdclass
return {
'zip_safe': parse_bool,
'include_package_data': parse_bool,
- 'package_dir': parse_dict,
+ 'package_dir': self._parse_dict,
'scripts': parse_list,
'eager_resources': parse_list,
'dependency_links': parse_list,
@@ -643,14 +650,14 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
"consider using implicit namespaces instead (PEP 420).",
# TODO: define due date, see setuptools.dist:check_nsp.
),
- 'install_requires': partial(
+ 'install_requires': partial( # Core Metadata
self._parse_requirements_list, "install_requires"
),
'setup_requires': self._parse_list_semicolon,
'packages': self._parse_packages,
'entry_points': self._parse_file_in_root,
'py_modules': parse_list,
- 'python_requires': SpecifierSet,
+ 'python_requires': _static.SpecifierSet, # Core Metadata
'cmdclass': parse_cmdclass,
}
@@ -727,7 +734,7 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
"""
self['exclude_package_data'] = self._parse_package_data(section_options)
- def parse_section_extras_require(self, section_options) -> None:
+ def parse_section_extras_require(self, section_options) -> None: # Core Metadata
"""Parses `extras_require` configuration file section.
:param dict section_options:
@@ -737,7 +744,8 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
lambda k, v: self._parse_requirements_list(f"extras_require[{k}]", v),
)
- self['extras_require'] = parsed
+ self['extras_require'] = _static.Dict(parsed)
+ # ^-- Use `_static.Dict` to mark a non-`Dynamic` Core Metadata
def parse_section_data_files(self, section_options) -> None:
"""Parses `data_files` configuration file section.
diff --git a/contrib/python/setuptools/py3/setuptools/dist.py b/contrib/python/setuptools/py3/setuptools/dist.py
index f878b2fa45..0249651267 100644
--- a/contrib/python/setuptools/py3/setuptools/dist.py
+++ b/contrib/python/setuptools/py3/setuptools/dist.py
@@ -19,6 +19,7 @@ from packaging.version import Version
from . import (
_entry_points,
_reqs,
+ _static,
command as _, # noqa: F401 # imported for side-effects
)
from ._importlib import metadata
@@ -391,10 +392,15 @@ class Distribution(_Distribution):
"""Make sure requirement-related attributes exist and are normalized"""
install_requires = getattr(self, "install_requires", None) or []
extras_require = getattr(self, "extras_require", None) or {}
- self.install_requires = list(map(str, _reqs.parse(install_requires)))
- self.extras_require = {
- k: list(map(str, _reqs.parse(v or []))) for k, v in extras_require.items()
- }
+
+ # Preserve the "static"-ness of values parsed from config files
+ list_ = _static.List if _static.is_static(install_requires) else list
+ self.install_requires = list_(map(str, _reqs.parse(install_requires)))
+
+ dict_ = _static.Dict if _static.is_static(extras_require) else dict
+ self.extras_require = dict_(
+ (k, list(map(str, _reqs.parse(v or [])))) for k, v in extras_require.items()
+ )
def _finalize_license_files(self) -> None:
"""Compute names of all license files which should be included."""
diff --git a/contrib/python/setuptools/py3/ya.make b/contrib/python/setuptools/py3/ya.make
index 85aa187c37..ab5826a9bf 100644
--- a/contrib/python/setuptools/py3/ya.make
+++ b/contrib/python/setuptools/py3/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(75.7.0)
+VERSION(75.8.0)
LICENSE(MIT)
@@ -93,6 +93,7 @@ PY_SRCS(
setuptools/_path.py
setuptools/_reqs.py
setuptools/_shutil.py
+ setuptools/_static.py
setuptools/archive_util.py
setuptools/build_meta.py
setuptools/command/__init__.py