up
This commit is contained in:
@@ -2,53 +2,45 @@
|
||||
Load setuptools configuration from ``setup.cfg`` files.
|
||||
|
||||
**API will be made private in the future**
|
||||
|
||||
To read project metadata, consider using
|
||||
``build.util.project_wheel_metadata`` (https://pypi.org/project/build/).
|
||||
For simple scenarios, you can also try parsing the file directly
|
||||
with the help of ``configparser``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
import os
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
import os
|
||||
import warnings
|
||||
from collections import defaultdict
|
||||
from collections.abc import Iterable, Iterator
|
||||
from functools import partial, wraps
|
||||
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, TypeVar, cast
|
||||
from functools import partial
|
||||
from functools import wraps
|
||||
from typing import (TYPE_CHECKING, Callable, Any, Dict, Generic, Iterable, List,
|
||||
Optional, Tuple, TypeVar, Union)
|
||||
|
||||
from packaging.markers import default_environment as marker_env
|
||||
from packaging.requirements import InvalidRequirement, Requirement
|
||||
from packaging.version import InvalidVersion, Version
|
||||
from distutils.errors import DistutilsOptionError, DistutilsFileError
|
||||
from setuptools.extern.packaging.requirements import Requirement, InvalidRequirement
|
||||
from setuptools.extern.packaging.version import Version, InvalidVersion
|
||||
from setuptools.extern.packaging.specifiers import SpecifierSet
|
||||
from setuptools._deprecation_warning import SetuptoolsDeprecationWarning
|
||||
|
||||
from .. import _static
|
||||
from .._path import StrPath
|
||||
from ..errors import FileError, OptionError
|
||||
from ..warnings import SetuptoolsDeprecationWarning
|
||||
from . import expand
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing_extensions import TypeAlias
|
||||
from setuptools.dist import Distribution # noqa
|
||||
from distutils.dist import DistributionMetadata # noqa
|
||||
|
||||
from setuptools.dist import Distribution
|
||||
|
||||
from distutils.dist import DistributionMetadata
|
||||
|
||||
SingleCommandOptions: TypeAlias = dict[str, tuple[str, Any]]
|
||||
_Path = Union[str, os.PathLike]
|
||||
SingleCommandOptions = Dict["str", Tuple["str", Any]]
|
||||
"""Dict that associate the name of the options of a particular command to a
|
||||
tuple. The first element of the tuple indicates the origin of the option value
|
||||
(e.g. the name of the configuration file where it was read from),
|
||||
while the second element of the tuple is the option value itself
|
||||
"""
|
||||
AllCommandOptions: TypeAlias = dict[str, SingleCommandOptions]
|
||||
"""cmd name => its options"""
|
||||
Target = TypeVar("Target", "Distribution", "DistributionMetadata")
|
||||
AllCommandOptions = Dict["str", SingleCommandOptions] # cmd name => its options
|
||||
Target = TypeVar("Target", bound=Union["Distribution", "DistributionMetadata"])
|
||||
|
||||
|
||||
def read_configuration(
|
||||
filepath: StrPath, find_others: bool = False, ignore_option_errors: bool = False
|
||||
filepath: _Path,
|
||||
find_others=False,
|
||||
ignore_option_errors=False
|
||||
) -> dict:
|
||||
"""Read given configuration file and returns options from it as a dict.
|
||||
|
||||
@@ -73,7 +65,7 @@ def read_configuration(
|
||||
return configuration_to_dict(handlers)
|
||||
|
||||
|
||||
def apply_configuration(dist: Distribution, filepath: StrPath) -> Distribution:
|
||||
def apply_configuration(dist: "Distribution", filepath: _Path) -> "Distribution":
|
||||
"""Apply the configuration from a ``setup.cfg`` file into an existing
|
||||
distribution object.
|
||||
"""
|
||||
@@ -83,26 +75,24 @@ def apply_configuration(dist: Distribution, filepath: StrPath) -> Distribution:
|
||||
|
||||
|
||||
def _apply(
|
||||
dist: Distribution,
|
||||
filepath: StrPath,
|
||||
other_files: Iterable[StrPath] = (),
|
||||
dist: "Distribution", filepath: _Path,
|
||||
other_files: Iterable[_Path] = (),
|
||||
ignore_option_errors: bool = False,
|
||||
) -> tuple[ConfigMetadataHandler, ConfigOptionsHandler]:
|
||||
) -> Tuple["ConfigHandler", ...]:
|
||||
"""Read configuration from ``filepath`` and applies to the ``dist`` object."""
|
||||
from setuptools.dist import _Distribution
|
||||
|
||||
filepath = os.path.abspath(filepath)
|
||||
|
||||
if not os.path.isfile(filepath):
|
||||
raise FileError(f'Configuration file {filepath} does not exist.')
|
||||
raise DistutilsFileError('Configuration file %s does not exist.' % filepath)
|
||||
|
||||
current_directory = os.getcwd()
|
||||
os.chdir(os.path.dirname(filepath))
|
||||
filenames = [*other_files, filepath]
|
||||
|
||||
try:
|
||||
# TODO: Temporary cast until mypy 1.12 is released with upstream fixes from typeshed
|
||||
_Distribution.parse_config_files(dist, filenames=cast(list[str], filenames))
|
||||
_Distribution.parse_config_files(dist, filenames=filenames)
|
||||
handlers = parse_configuration(
|
||||
dist, dist.command_options, ignore_option_errors=ignore_option_errors
|
||||
)
|
||||
@@ -113,26 +103,22 @@ def _apply(
|
||||
return handlers
|
||||
|
||||
|
||||
def _get_option(target_obj: Distribution | DistributionMetadata, key: str):
|
||||
def _get_option(target_obj: Target, key: str):
|
||||
"""
|
||||
Given a target object and option key, get that option from
|
||||
the target object, either through a get_{key} method or
|
||||
from an attribute directly.
|
||||
"""
|
||||
getter_name = f'get_{key}'
|
||||
getter_name = 'get_{key}'.format(**locals())
|
||||
by_attribute = functools.partial(getattr, target_obj, key)
|
||||
getter = getattr(target_obj, getter_name, by_attribute)
|
||||
return getter()
|
||||
|
||||
|
||||
def configuration_to_dict(
|
||||
handlers: Iterable[
|
||||
ConfigHandler[Distribution] | ConfigHandler[DistributionMetadata]
|
||||
],
|
||||
) -> dict:
|
||||
def configuration_to_dict(handlers: Tuple["ConfigHandler", ...]) -> dict:
|
||||
"""Returns configuration data gathered by given handlers as a dict.
|
||||
|
||||
:param Iterable[ConfigHandler] handlers: Handlers list,
|
||||
:param list[ConfigHandler] handlers: Handlers list,
|
||||
usually from parse_configuration()
|
||||
|
||||
:rtype: dict
|
||||
@@ -148,10 +134,10 @@ def configuration_to_dict(
|
||||
|
||||
|
||||
def parse_configuration(
|
||||
distribution: Distribution,
|
||||
distribution: "Distribution",
|
||||
command_options: AllCommandOptions,
|
||||
ignore_option_errors: bool = False,
|
||||
) -> tuple[ConfigMetadataHandler, ConfigOptionsHandler]:
|
||||
ignore_option_errors=False
|
||||
) -> Tuple["ConfigMetadataHandler", "ConfigOptionsHandler"]:
|
||||
"""Performs additional parsing of configuration options
|
||||
for a distribution.
|
||||
|
||||
@@ -186,9 +172,6 @@ def parse_configuration(
|
||||
distribution.src_root,
|
||||
)
|
||||
meta.parse()
|
||||
distribution._referenced_files.update(
|
||||
options._referenced_files, meta._referenced_files
|
||||
)
|
||||
|
||||
return meta, options
|
||||
|
||||
@@ -213,16 +196,17 @@ def _warn_accidental_env_marker_misconfig(label: str, orig_value: str, parsed: l
|
||||
if "\n" in orig_value or len(parsed) != 2:
|
||||
return
|
||||
|
||||
markers = marker_env().keys()
|
||||
|
||||
try:
|
||||
req = Requirement(parsed[1])
|
||||
if req.name in markers:
|
||||
_AmbiguousMarker.emit(field=label, req=parsed[1])
|
||||
except InvalidRequirement as ex:
|
||||
if any(parsed[1].startswith(marker) for marker in markers):
|
||||
msg = _AmbiguousMarker.message(field=label, req=parsed[1])
|
||||
raise InvalidRequirement(msg) from ex
|
||||
with contextlib.suppress(InvalidRequirement):
|
||||
original_requirements_str = ";".join(parsed)
|
||||
req = Requirement(original_requirements_str)
|
||||
if req.marker is not None:
|
||||
msg = (
|
||||
f"One of the parsed requirements in `{label}` "
|
||||
f"looks like a valid environment marker: '{parsed[1]}'\n"
|
||||
"Make sure that the config is correct and check "
|
||||
"https://setuptools.pypa.io/en/latest/userguide/declarative_config.html#opt-2" # noqa: E501
|
||||
)
|
||||
warnings.warn(msg, UserWarning)
|
||||
|
||||
|
||||
class ConfigHandler(Generic[Target]):
|
||||
@@ -234,7 +218,7 @@ class ConfigHandler(Generic[Target]):
|
||||
|
||||
"""
|
||||
|
||||
aliases: ClassVar[dict[str, str]] = {}
|
||||
aliases: Dict[str, str] = {}
|
||||
"""Options aliases.
|
||||
For compatibility with various packages. E.g.: d2to1 and pbr.
|
||||
Note: `-` in keys is replaced with `_` by config parser.
|
||||
@@ -247,57 +231,65 @@ class ConfigHandler(Generic[Target]):
|
||||
options: AllCommandOptions,
|
||||
ignore_option_errors,
|
||||
ensure_discovered: expand.EnsurePackagesDiscovered,
|
||||
) -> None:
|
||||
self.ignore_option_errors = ignore_option_errors
|
||||
self.target_obj: Target = target_obj
|
||||
self.sections = dict(self._section_options(options))
|
||||
self.set_options: list[str] = []
|
||||
self.ensure_discovered = ensure_discovered
|
||||
self._referenced_files = set[str]()
|
||||
"""After parsing configurations, this property will enumerate
|
||||
all files referenced by the "file:" directive. Private API for setuptools only.
|
||||
"""
|
||||
):
|
||||
sections: AllCommandOptions = {}
|
||||
|
||||
@classmethod
|
||||
def _section_options(
|
||||
cls, options: AllCommandOptions
|
||||
) -> Iterator[tuple[str, SingleCommandOptions]]:
|
||||
for full_name, value in options.items():
|
||||
pre, _sep, name = full_name.partition(cls.section_prefix)
|
||||
if pre:
|
||||
section_prefix = self.section_prefix
|
||||
for section_name, section_options in options.items():
|
||||
if not section_name.startswith(section_prefix):
|
||||
continue
|
||||
yield name.lstrip('.'), value
|
||||
|
||||
section_name = section_name.replace(section_prefix, '').strip('.')
|
||||
sections[section_name] = section_options
|
||||
|
||||
self.ignore_option_errors = ignore_option_errors
|
||||
self.target_obj = target_obj
|
||||
self.sections = sections
|
||||
self.set_options: List[str] = []
|
||||
self.ensure_discovered = ensure_discovered
|
||||
|
||||
@property
|
||||
def parsers(self):
|
||||
"""Metadata item name to parser function mapping."""
|
||||
raise NotImplementedError(
|
||||
f'{self.__class__.__name__} must provide .parsers property'
|
||||
'%s must provide .parsers property' % self.__class__.__name__
|
||||
)
|
||||
|
||||
def __setitem__(self, option_name, value) -> None:
|
||||
def __setitem__(self, option_name, value):
|
||||
unknown = tuple()
|
||||
target_obj = self.target_obj
|
||||
|
||||
# Translate alias into real name.
|
||||
option_name = self.aliases.get(option_name, option_name)
|
||||
|
||||
try:
|
||||
current_value = getattr(target_obj, option_name)
|
||||
except AttributeError as e:
|
||||
raise KeyError(option_name) from e
|
||||
current_value = getattr(target_obj, option_name, unknown)
|
||||
|
||||
if current_value is unknown:
|
||||
raise KeyError(option_name)
|
||||
|
||||
if current_value:
|
||||
# Already inhabited. Skipping.
|
||||
return
|
||||
|
||||
try:
|
||||
parsed = self.parsers.get(option_name, lambda x: x)(value)
|
||||
except (Exception,) * self.ignore_option_errors:
|
||||
skip_option = False
|
||||
parser = self.parsers.get(option_name)
|
||||
if parser:
|
||||
try:
|
||||
value = parser(value)
|
||||
|
||||
except Exception:
|
||||
skip_option = True
|
||||
if not self.ignore_option_errors:
|
||||
raise
|
||||
|
||||
if skip_option:
|
||||
return
|
||||
|
||||
simple_setter = functools.partial(target_obj.__setattr__, option_name)
|
||||
setter = getattr(target_obj, f"set_{option_name}", simple_setter)
|
||||
setter(parsed)
|
||||
setter = getattr(target_obj, 'set_%s' % option_name, None)
|
||||
if setter is None:
|
||||
setattr(target_obj, option_name, value)
|
||||
else:
|
||||
setter(value)
|
||||
|
||||
self.set_options.append(option_name)
|
||||
|
||||
@@ -333,7 +325,9 @@ class ConfigHandler(Generic[Target]):
|
||||
for line in cls._parse_list(value):
|
||||
key, sep, val = line.partition(separator)
|
||||
if sep != separator:
|
||||
raise OptionError(f"Unable to parse option value to dict: {value}")
|
||||
raise DistutilsOptionError(
|
||||
'Unable to parse option value to dict: %s' % value
|
||||
)
|
||||
result[key.strip()] = val.strip()
|
||||
|
||||
return result
|
||||
@@ -364,14 +358,15 @@ class ConfigHandler(Generic[Target]):
|
||||
exclude_directive = 'file:'
|
||||
if value.startswith(exclude_directive):
|
||||
raise ValueError(
|
||||
f'Only strings are accepted for the {key} field, '
|
||||
'files are not accepted'
|
||||
'Only strings are accepted for the {0} field, '
|
||||
'files are not accepted'.format(key)
|
||||
)
|
||||
return _static.Str(value)
|
||||
return value
|
||||
|
||||
return parser
|
||||
|
||||
def _parse_file(self, value, root_dir: StrPath | None):
|
||||
@classmethod
|
||||
def _parse_file(cls, value, root_dir: _Path):
|
||||
"""Represents value as a string, allowing including text
|
||||
from nearest files using `file:` directive.
|
||||
|
||||
@@ -390,15 +385,13 @@ class ConfigHandler(Generic[Target]):
|
||||
return value
|
||||
|
||||
if not value.startswith(include_directive):
|
||||
return _static.Str(value)
|
||||
return value
|
||||
|
||||
spec = value[len(include_directive) :]
|
||||
filepaths = [path.strip() for path in spec.split(',')]
|
||||
self._referenced_files.update(filepaths)
|
||||
# XXX: Is marking as static contents coming from files too optimistic?
|
||||
return _static.Str(expand.read_files(filepaths, root_dir))
|
||||
filepaths = (path.strip() for path in spec.split(','))
|
||||
return expand.read_files(filepaths, root_dir)
|
||||
|
||||
def _parse_attr(self, value, package_dir, root_dir: StrPath):
|
||||
def _parse_attr(self, value, package_dir, root_dir: _Path):
|
||||
"""Represents value as a module attribute.
|
||||
|
||||
Examples:
|
||||
@@ -410,7 +403,7 @@ class ConfigHandler(Generic[Target]):
|
||||
"""
|
||||
attr_directive = 'attr:'
|
||||
if not value.startswith(attr_directive):
|
||||
return _static.Str(value)
|
||||
return value
|
||||
|
||||
attr_desc = value.replace(attr_directive, '')
|
||||
|
||||
@@ -466,58 +459,60 @@ class ConfigHandler(Generic[Target]):
|
||||
parser = (lambda _, v: values_parser(v)) if values_parser else (lambda _, v: v)
|
||||
return cls._parse_section_to_dict_with_key(section_options, parser)
|
||||
|
||||
def parse_section(self, section_options) -> None:
|
||||
def parse_section(self, section_options):
|
||||
"""Parses configuration file section.
|
||||
|
||||
:param dict section_options:
|
||||
"""
|
||||
for name, (_, value) in section_options.items():
|
||||
for (name, (_, value)) in section_options.items():
|
||||
with contextlib.suppress(KeyError):
|
||||
# Keep silent for a new option may appear anytime.
|
||||
self[name] = value
|
||||
|
||||
def parse(self) -> None:
|
||||
def parse(self):
|
||||
"""Parses configuration file items from one
|
||||
or more related sections.
|
||||
|
||||
"""
|
||||
for section_name, section_options in self.sections.items():
|
||||
|
||||
method_postfix = ''
|
||||
if section_name: # [section.option] variant
|
||||
method_postfix = f"_{section_name}"
|
||||
method_postfix = '_%s' % section_name
|
||||
|
||||
section_parser_method: Callable | None = getattr(
|
||||
section_parser_method: Optional[Callable] = getattr(
|
||||
self,
|
||||
# Dots in section names are translated into dunderscores.
|
||||
f'parse_section{method_postfix}'.replace('.', '__'),
|
||||
('parse_section%s' % method_postfix).replace('.', '__'),
|
||||
None,
|
||||
)
|
||||
|
||||
if section_parser_method is None:
|
||||
raise OptionError(
|
||||
"Unsupported distribution option section: "
|
||||
f"[{self.section_prefix}.{section_name}]"
|
||||
raise DistutilsOptionError(
|
||||
'Unsupported distribution option section: [%s.%s]'
|
||||
% (self.section_prefix, section_name)
|
||||
)
|
||||
|
||||
section_parser_method(section_options)
|
||||
|
||||
def _deprecated_config_handler(self, func, msg, **kw):
|
||||
def _deprecated_config_handler(self, func, msg, warning_class):
|
||||
"""this function will wrap around parameters that are deprecated
|
||||
|
||||
:param msg: deprecation message
|
||||
:param warning_class: class of warning exception to be raised
|
||||
:param func: function to be wrapped around
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def config_handler(*args, **kwargs):
|
||||
kw.setdefault("stacklevel", 2)
|
||||
_DeprecatedConfig.emit("Deprecated config in `setup.cfg`", msg, **kw)
|
||||
warnings.warn(msg, warning_class)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return config_handler
|
||||
|
||||
|
||||
class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
|
||||
|
||||
section_prefix = 'metadata'
|
||||
|
||||
aliases = {
|
||||
@@ -535,13 +530,13 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
target_obj: DistributionMetadata,
|
||||
target_obj: "DistributionMetadata",
|
||||
options: AllCommandOptions,
|
||||
ignore_option_errors: bool,
|
||||
ensure_discovered: expand.EnsurePackagesDiscovered,
|
||||
package_dir: dict | None = None,
|
||||
root_dir: StrPath | None = os.curdir,
|
||||
) -> None:
|
||||
package_dir: Optional[dict] = None,
|
||||
root_dir: _Path = os.curdir
|
||||
):
|
||||
super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
|
||||
self.package_dir = package_dir
|
||||
self.root_dir = root_dir
|
||||
@@ -549,29 +544,35 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
|
||||
@property
|
||||
def parsers(self):
|
||||
"""Metadata item name to parser function mapping."""
|
||||
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_list = self._parse_list
|
||||
parse_file = partial(self._parse_file, root_dir=self.root_dir)
|
||||
parse_dict = self._parse_dict
|
||||
exclude_files_parser = self._exclude_files_parser
|
||||
|
||||
return {
|
||||
'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),
|
||||
'platforms': parse_list,
|
||||
'keywords': parse_list,
|
||||
'provides': parse_list,
|
||||
'requires': self._deprecated_config_handler(
|
||||
parse_list,
|
||||
"The requires parameter is deprecated, please use "
|
||||
"install_requires for runtime dependencies.",
|
||||
SetuptoolsDeprecationWarning,
|
||||
),
|
||||
'obsoletes': parse_list,
|
||||
'classifiers': self._get_parser_compound(parse_file, parse_list),
|
||||
'license': exclude_files_parser('license'),
|
||||
'license_files': parse_list_static,
|
||||
'license_file': self._deprecated_config_handler(
|
||||
exclude_files_parser('license_file'),
|
||||
"The license_file parameter is deprecated, "
|
||||
"use license_files instead.",
|
||||
SetuptoolsDeprecationWarning,
|
||||
),
|
||||
'license_files': parse_list,
|
||||
'description': parse_file,
|
||||
'long_description': parse_file,
|
||||
'long_description_content_type': _static.Str,
|
||||
'version': self._parse_version, # Cannot be marked as dynamic
|
||||
'url': _static.Str,
|
||||
'project_urls': parse_dict_static,
|
||||
'version': self._parse_version,
|
||||
'project_urls': parse_dict,
|
||||
}
|
||||
|
||||
def _parse_version(self, value):
|
||||
@@ -589,11 +590,12 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
|
||||
# accidentally include newlines and other unintended content
|
||||
try:
|
||||
Version(version)
|
||||
except InvalidVersion as e:
|
||||
raise OptionError(
|
||||
f'Version loaded from {value} does not '
|
||||
f'comply with PEP 440: {version}'
|
||||
) from e
|
||||
except InvalidVersion:
|
||||
tmpl = (
|
||||
'Version loaded from {value} does not '
|
||||
'comply with PEP 440: {version}'
|
||||
)
|
||||
raise DistutilsOptionError(tmpl.format(**locals()))
|
||||
|
||||
return version
|
||||
|
||||
@@ -601,18 +603,19 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
|
||||
|
||||
|
||||
class ConfigOptionsHandler(ConfigHandler["Distribution"]):
|
||||
|
||||
section_prefix = 'options'
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
target_obj: Distribution,
|
||||
target_obj: "Distribution",
|
||||
options: AllCommandOptions,
|
||||
ignore_option_errors: bool,
|
||||
ensure_discovered: expand.EnsurePackagesDiscovered,
|
||||
) -> None:
|
||||
):
|
||||
super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
|
||||
self.root_dir = target_obj.src_root
|
||||
self.package_dir: dict[str, str] = {} # To be filled by `find_packages`
|
||||
self.package_dir: Dict[str, str] = {} # To be filled by `find_packages`
|
||||
|
||||
@classmethod
|
||||
def _parse_list_semicolon(cls, value):
|
||||
@@ -627,20 +630,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 _static.List(line for line in parsed if not line.startswith("#"))
|
||||
# ^-- Use `_static.List` to mark a non-`Dynamic` Core Metadata
|
||||
return [line for line in parsed if not line.startswith("#")]
|
||||
|
||||
@property
|
||||
def parsers(self):
|
||||
"""Metadata item name to parser function mapping."""
|
||||
parse_list = self._parse_list
|
||||
parse_bool = self._parse_bool
|
||||
parse_dict = self._parse_dict
|
||||
parse_cmdclass = self._parse_cmdclass
|
||||
|
||||
return {
|
||||
'zip_safe': parse_bool,
|
||||
'include_package_data': parse_bool,
|
||||
'package_dir': self._parse_dict,
|
||||
'package_dir': parse_dict,
|
||||
'scripts': parse_list,
|
||||
'eager_resources': parse_list,
|
||||
'dependency_links': parse_list,
|
||||
@@ -648,16 +651,17 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
|
||||
parse_list,
|
||||
"The namespace_packages parameter is deprecated, "
|
||||
"consider using implicit namespaces instead (PEP 420).",
|
||||
# TODO: define due date, see setuptools.dist:check_nsp.
|
||||
SetuptoolsDeprecationWarning,
|
||||
),
|
||||
'install_requires': partial( # Core Metadata
|
||||
'install_requires': partial(
|
||||
self._parse_requirements_list, "install_requires"
|
||||
),
|
||||
'setup_requires': self._parse_list_semicolon,
|
||||
'tests_require': self._parse_list_semicolon,
|
||||
'packages': self._parse_packages,
|
||||
'entry_points': self._parse_file_in_root,
|
||||
'py_modules': parse_list,
|
||||
'python_requires': _static.SpecifierSet, # Core Metadata
|
||||
'python_requires': SpecifierSet,
|
||||
'cmdclass': parse_cmdclass,
|
||||
}
|
||||
|
||||
@@ -700,7 +704,10 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
|
||||
section_data = self._parse_section_to_dict(section_options, self._parse_list)
|
||||
|
||||
valid_keys = ['where', 'include', 'exclude']
|
||||
find_kwargs = {k: v for k, v in section_data.items() if k in valid_keys and v}
|
||||
|
||||
find_kwargs = dict(
|
||||
[(k, v) for k, v in section_data.items() if k in valid_keys and v]
|
||||
)
|
||||
|
||||
where = find_kwargs.get('where')
|
||||
if where is not None:
|
||||
@@ -708,7 +715,7 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
|
||||
|
||||
return find_kwargs
|
||||
|
||||
def parse_section_entry_points(self, section_options) -> None:
|
||||
def parse_section_entry_points(self, section_options):
|
||||
"""Parses `entry_points` configuration file section.
|
||||
|
||||
:param dict section_options:
|
||||
@@ -720,61 +727,36 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
|
||||
package_data = self._parse_section_to_dict(section_options, self._parse_list)
|
||||
return expand.canonic_package_data(package_data)
|
||||
|
||||
def parse_section_package_data(self, section_options) -> None:
|
||||
def parse_section_package_data(self, section_options):
|
||||
"""Parses `package_data` configuration file section.
|
||||
|
||||
:param dict section_options:
|
||||
"""
|
||||
self['package_data'] = self._parse_package_data(section_options)
|
||||
|
||||
def parse_section_exclude_package_data(self, section_options) -> None:
|
||||
def parse_section_exclude_package_data(self, section_options):
|
||||
"""Parses `exclude_package_data` configuration file section.
|
||||
|
||||
:param dict section_options:
|
||||
"""
|
||||
self['exclude_package_data'] = self._parse_package_data(section_options)
|
||||
|
||||
def parse_section_extras_require(self, section_options) -> None: # Core Metadata
|
||||
def parse_section_extras_require(self, section_options):
|
||||
"""Parses `extras_require` configuration file section.
|
||||
|
||||
:param dict section_options:
|
||||
"""
|
||||
parsed = self._parse_section_to_dict_with_key(
|
||||
section_options,
|
||||
lambda k, v: self._parse_requirements_list(f"extras_require[{k}]", v),
|
||||
lambda k, v: self._parse_requirements_list(f"extras_require[{k}]", v)
|
||||
)
|
||||
|
||||
self['extras_require'] = _static.Dict(parsed)
|
||||
# ^-- Use `_static.Dict` to mark a non-`Dynamic` Core Metadata
|
||||
self['extras_require'] = parsed
|
||||
|
||||
def parse_section_data_files(self, section_options) -> None:
|
||||
def parse_section_data_files(self, section_options):
|
||||
"""Parses `data_files` configuration file section.
|
||||
|
||||
:param dict section_options:
|
||||
"""
|
||||
parsed = self._parse_section_to_dict(section_options, self._parse_list)
|
||||
self['data_files'] = expand.canonic_data_files(parsed, self.root_dir)
|
||||
|
||||
|
||||
class _AmbiguousMarker(SetuptoolsDeprecationWarning):
|
||||
_SUMMARY = "Ambiguous requirement marker."
|
||||
_DETAILS = """
|
||||
One of the parsed requirements in `{field}` looks like a valid environment marker:
|
||||
|
||||
{req!r}
|
||||
|
||||
Please make sure that the configuration file is correct.
|
||||
You can use dangling lines to avoid this problem.
|
||||
"""
|
||||
_SEE_DOCS = "userguide/declarative_config.html#opt-2"
|
||||
# TODO: should we include due_date here? Initially introduced in 6 Aug 2022.
|
||||
# Does this make sense with latest version of packaging?
|
||||
|
||||
@classmethod
|
||||
def message(cls, **kw):
|
||||
docs = f"https://setuptools.pypa.io/en/latest/{cls._SEE_DOCS}"
|
||||
return cls._format(cls._SUMMARY, cls._DETAILS, see_url=docs, format_args=kw)
|
||||
|
||||
|
||||
class _DeprecatedConfig(SetuptoolsDeprecationWarning):
|
||||
_SEE_DOCS = "userguide/declarative_config.html"
|
||||
|
||||
Reference in New Issue
Block a user