summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnderson Bravalheri <andersonbravalheri@gmail.com>2023-03-06 21:04:32 +0000
committerAnderson Bravalheri <andersonbravalheri@gmail.com>2023-03-07 21:48:37 +0000
commit93c4862e2274fff7263937ce4175380b14e30cf5 (patch)
tree760b728170f0385950af129b29abdf8615d6486d
parenta05e81ae0cea8ed4b5bf1f43ea82c5af75e4f6e3 (diff)
downloadpython-setuptools-git-93c4862e2274fff7263937ce4175380b14e30cf5.tar.gz
Use new warnings in setuptools/config/setupcfg.py
-rw-r--r--setuptools/config/setupcfg.py93
-rw-r--r--setuptools/tests/config/test_setupcfg.py24
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')