diff options
| author | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2023-03-06 21:04:32 +0000 |
|---|---|---|
| committer | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2023-03-07 21:48:37 +0000 |
| commit | 93c4862e2274fff7263937ce4175380b14e30cf5 (patch) | |
| tree | 760b728170f0385950af129b29abdf8615d6486d | |
| parent | a05e81ae0cea8ed4b5bf1f43ea82c5af75e4f6e3 (diff) | |
| download | python-setuptools-git-93c4862e2274fff7263937ce4175380b14e30cf5.tar.gz | |
Use new warnings in setuptools/config/setupcfg.py
| -rw-r--r-- | setuptools/config/setupcfg.py | 93 | ||||
| -rw-r--r-- | setuptools/tests/config/test_setupcfg.py | 24 |
2 files changed, 66 insertions, 51 deletions
diff --git a/setuptools/config/setupcfg.py b/setuptools/config/setupcfg.py index 8c0dcc61..050e5385 100644 --- a/setuptools/config/setupcfg.py +++ b/setuptools/config/setupcfg.py @@ -2,12 +2,15 @@ Load setuptools configuration from ``setup.cfg`` files. **API will be made private in the future** -""" -import os +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``. +""" import contextlib import functools -import warnings +import os from collections import defaultdict from functools import partial from functools import wraps @@ -26,19 +29,19 @@ from typing import ( Union, ) -from distutils.errors import DistutilsOptionError, DistutilsFileError -from setuptools.extern.packaging.requirements import Requirement, InvalidRequirement -from setuptools.extern.packaging.markers import default_environment as marker_env -from setuptools.extern.packaging.version import Version, InvalidVersion -from setuptools.extern.packaging.specifiers import SpecifierSet -from setuptools.warnings import SetuptoolsDeprecationWarning - +from ..errors import FileError, OptionError +from ..extern.packaging.markers import default_environment as marker_env +from ..extern.packaging.requirements import InvalidRequirement, Requirement +from ..extern.packaging.specifiers import SpecifierSet +from ..extern.packaging.version import InvalidVersion, Version +from ..warnings import SetuptoolsDeprecationWarning from . import expand if TYPE_CHECKING: - from setuptools.dist import Distribution # noqa from distutils.dist import DistributionMetadata # noqa + from setuptools.dist import Distribution # noqa + _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 @@ -97,7 +100,7 @@ def _apply( filepath = os.path.abspath(filepath) if not os.path.isfile(filepath): - raise DistutilsFileError('Configuration file %s does not exist.' % filepath) + raise FileError(f'Configuration file {filepath} does not exist.') current_directory = os.getcwd() os.chdir(os.path.dirname(filepath)) @@ -121,7 +124,7 @@ def _get_option(target_obj: Target, key: str): the target object, either through a get_{key} method or from an attribute directly. """ - getter_name = 'get_{key}'.format(**locals()) + getter_name = f'get_{key}' by_attribute = functools.partial(getattr, target_obj, key) getter = getattr(target_obj, getter_name, by_attribute) return getter() @@ -212,19 +215,14 @@ def _warn_accidental_env_marker_misconfig(label: str, orig_value: str, parsed: l return markers = marker_env().keys() - 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 - ) try: req = Requirement(parsed[1]) if req.name in markers: - warnings.warn(msg) + _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 @@ -334,9 +332,7 @@ class ConfigHandler(Generic[Target]): for line in cls._parse_list(value): key, sep, val = line.partition(separator) if sep != separator: - raise DistutilsOptionError( - 'Unable to parse option value to dict: %s' % value - ) + raise OptionError(f"Unable to parse option value to dict: {value}") result[key.strip()] = val.strip() return result @@ -496,24 +492,24 @@ class ConfigHandler(Generic[Target]): ) if section_parser_method is None: - raise DistutilsOptionError( - 'Unsupported distribution option section: [%s.%s]' - % (self.section_prefix, section_name) + raise OptionError( + "Unsupported distribution option section: " + f"[{self.section_prefix}.{section_name}]" ) section_parser_method(section_options) - def _deprecated_config_handler(self, func, msg, warning_class): + def _deprecated_config_handler(self, func, msg, **kw): """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): - warnings.warn(msg, warning_class) + kw.setdefault("stacklevel", 2) + _DeprecatedConfig.emit("Deprecated config in `setup.cfg`", msg, **kw) return func(*args, **kwargs) return config_handler @@ -564,7 +560,8 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]): parse_list, "The requires parameter is deprecated, please use " "install_requires for runtime dependencies.", - SetuptoolsDeprecationWarning, + due_date=(2023, 10, 30), + # Warning introduced in 27 Oct 2018 ), 'obsoletes': parse_list, 'classifiers': self._get_parser_compound(parse_file, parse_list), @@ -573,7 +570,8 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]): exclude_files_parser('license_file'), "The license_file parameter is deprecated, " "use license_files instead.", - SetuptoolsDeprecationWarning, + due_date=(2023, 10, 30), + # Warning introduced in 23 May 2021 ), 'license_files': parse_list, 'description': parse_file, @@ -598,11 +596,10 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]): try: Version(version) except InvalidVersion: - tmpl = ( - 'Version loaded from {value} does not ' - 'comply with PEP 440: {version}' + raise OptionError( + f'Version loaded from {value} does not ' + f'comply with PEP 440: {version}' ) - raise DistutilsOptionError(tmpl.format(**locals())) return version @@ -657,7 +654,7 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]): parse_list, "The namespace_packages parameter is deprecated, " "consider using implicit namespaces instead (PEP 420).", - SetuptoolsDeprecationWarning, + # TODO: define due date, see setuptools.dist:check_nsp. ), 'install_requires': partial( self._parse_requirements_list, "install_requires" @@ -766,3 +763,27 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]): """ 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 = "https://setuptools.pypa.io/en/latest/userguide/declarative_config.html" diff --git a/setuptools/tests/config/test_setupcfg.py b/setuptools/tests/config/test_setupcfg.py index 97207829..69b75a89 100644 --- a/setuptools/tests/config/test_setupcfg.py +++ b/setuptools/tests/config/test_setupcfg.py @@ -7,10 +7,10 @@ from unittest.mock import Mock, patch import pytest from distutils.errors import DistutilsOptionError, DistutilsFileError -from setuptools.warnings import SetuptoolsDeprecationWarning from setuptools.dist import Distribution, _Distribution from setuptools.config.setupcfg import ConfigHandler, read_configuration from setuptools.extern.packaging.requirements import InvalidRequirement +from setuptools.warnings import SetuptoolsDeprecationWarning from ..textwrap import DALS @@ -468,12 +468,8 @@ class TestMetadata: 'author-email = test@test.com\n' 'maintainer_email = foo@foo.com\n', ) - msg = ( - "Usage of dash-separated 'author-email' will not be supported " - "in future versions. " - "Please use the underscore name 'author_email' instead" - ) - with pytest.warns(UserWarning, match=msg): + msg = "Usage of dash-separated 'author-email' will not be supported" + with pytest.warns(SetuptoolsDeprecationWarning, match=msg): with get_dist(tmpdir) as dist: metadata = dist.metadata @@ -486,12 +482,8 @@ class TestMetadata: fake_env( tmpdir, '[metadata]\n' 'Name = foo\n' 'description = Some description\n' ) - msg = ( - "Usage of uppercase key 'Name' in 'metadata' will be deprecated in " - "future versions. " - "Please use lowercase 'name' instead" - ) - with pytest.warns(UserWarning, match=msg): + msg = "Usage of uppercase key 'Name' in 'metadata' will not be supported" + with pytest.warns(SetuptoolsDeprecationWarning, match=msg): with get_dist(tmpdir) as dist: metadata = dist.metadata @@ -755,7 +747,7 @@ class TestOptions: r"One of the parsed requirements in `(install_requires|extras_require.+)` " "looks like a valid environment marker.*" ) - with pytest.warns(UserWarning, match=match): + with pytest.warns(SetuptoolsDeprecationWarning, match=match): with get_dist(tmpdir) as _: pass @@ -774,12 +766,14 @@ class TestOptions: "[options]\ninstall_requires =\n bar\n python_version<3\n", ], ) + @pytest.mark.filterwarnings("error::setuptools.SetuptoolsDeprecationWarning") def test_nowarn_accidental_env_marker_misconfig(self, config, tmpdir, recwarn): fake_env(tmpdir, config) + num_warnings = len(recwarn) with get_dist(tmpdir) as _: pass # The examples are valid, no warnings shown - assert not any(w.category == UserWarning for w in recwarn) + assert len(recwarn) == num_warnings def test_dash_preserved_extras_require(self, tmpdir): fake_env(tmpdir, '[options.extras_require]\n' 'foo-a = foo\n' 'foo_b = test\n') |
