Skip to content

Commit e7c42a0

Browse files
authored
Merge pull request #4870 from abravalheri/issue-4864
Disallow deprecated dash-separated and uppercase options in setup.cfg
2 parents 25a83fa + d364057 commit e7c42a0

File tree

3 files changed

+85
-82
lines changed

3 files changed

+85
-82
lines changed

newsfragments/4870.removal.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Setuptools no longer accepts options containing uppercase or dash characters in ``setup.cfg``.
2+
Please ensure to write the options in ``setup.cfg`` using the :wiki:`lower_snake_case <Snake_case>` convention
3+
(e.g. ``Name => name``, ``install-requires => install_requires``).
4+
This is a follow-up on deprecations introduced in
5+
`v54.1.0 <https://setuptools.pypa.io/en/latest/history.html#v54-1-0>`_ (see #1608) and
6+
`v54.1.1 <https://setuptools.pypa.io/en/latest/history.html#v54-1-1>`_ (see #2592).
7+
8+
.. note::
9+
This change *does not affect configurations in* ``pyproject.toml``
10+
(which uses the :wiki:`lower-kebab-case <Letter_case#Kebab_case>` convention following the precedent set in :pep:`517`/:pep:`518`).

setuptools/dist.py

Lines changed: 43 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import functools
34
import io
45
import itertools
56
import numbers
@@ -595,8 +596,8 @@ def _parse_config_files(self, filenames=None): # noqa: C901
595596
continue
596597

597598
val = parser.get(section, opt)
598-
opt = self.warn_dash_deprecation(opt, section)
599-
opt = self.make_option_lowercase(opt, section)
599+
opt = self._enforce_underscore(opt, section)
600+
opt = self._enforce_option_lowercase(opt, section)
600601
opt_dict[opt] = (filename, val)
601602

602603
# Make the ConfigParser forget everything (so we retain
@@ -621,64 +622,42 @@ def _parse_config_files(self, filenames=None): # noqa: C901
621622
except ValueError as e:
622623
raise DistutilsOptionError(e) from e
623624

624-
def warn_dash_deprecation(self, opt: str, section: str) -> str:
625-
if section in (
626-
'options.extras_require',
627-
'options.data_files',
628-
):
625+
def _enforce_underscore(self, opt: str, section: str) -> str:
626+
if "-" not in opt or self._skip_setupcfg_normalization(section):
629627
return opt
630628

631-
underscore_opt = opt.replace('-', '_')
632-
commands = list(
633-
itertools.chain(
634-
distutils.command.__all__,
635-
self._setuptools_commands(),
636-
)
629+
raise InvalidConfigError(
630+
f"Invalid dash-separated key {opt!r} in {section!r} (setup.cfg), "
631+
f"please use the underscore name {opt.replace('-', '_')!r} instead."
632+
# Warning initially introduced in 3 Mar 2021
637633
)
638-
if (
639-
not section.startswith('options')
640-
and section != 'metadata'
641-
and section not in commands
642-
):
643-
return underscore_opt
644-
645-
if '-' in opt:
646-
SetuptoolsDeprecationWarning.emit(
647-
"Invalid dash-separated options",
648-
f"""
649-
Usage of dash-separated {opt!r} will not be supported in future
650-
versions. Please use the underscore name {underscore_opt!r} instead.
651-
""",
652-
see_docs="userguide/declarative_config.html",
653-
due_date=(2025, 3, 3),
654-
# Warning initially introduced in 3 Mar 2021
655-
)
656-
return underscore_opt
657634

658-
def _setuptools_commands(self):
659-
try:
660-
entry_points = metadata.distribution('setuptools').entry_points
661-
return {ep.name for ep in entry_points} # Avoid newer API for compatibility
662-
except metadata.PackageNotFoundError:
663-
# during bootstrapping, distribution doesn't exist
664-
return []
665-
666-
def make_option_lowercase(self, opt: str, section: str) -> str:
667-
if section != 'metadata' or opt.islower():
635+
def _enforce_option_lowercase(self, opt: str, section: str) -> str:
636+
if opt.islower() or self._skip_setupcfg_normalization(section):
668637
return opt
669638

670-
lowercase_opt = opt.lower()
671-
SetuptoolsDeprecationWarning.emit(
672-
"Invalid uppercase configuration",
673-
f"""
674-
Usage of uppercase key {opt!r} in {section!r} will not be supported in
675-
future versions. Please use lowercase {lowercase_opt!r} instead.
676-
""",
677-
see_docs="userguide/declarative_config.html",
678-
due_date=(2025, 3, 3),
639+
raise InvalidConfigError(
640+
f"Invalid uppercase key {opt!r} in {section!r} (setup.cfg), "
641+
f"please use lowercase {opt.lower()!r} instead."
679642
# Warning initially introduced in 6 Mar 2021
680643
)
681-
return lowercase_opt
644+
645+
def _skip_setupcfg_normalization(self, section: str) -> bool:
646+
skip = (
647+
'options.extras_require',
648+
'options.data_files',
649+
'options.entry_points',
650+
'options.package_data',
651+
'options.exclude_package_data',
652+
)
653+
return section in skip or not self._is_setuptools_section(section)
654+
655+
def _is_setuptools_section(self, section: str) -> bool:
656+
return (
657+
section == "metadata"
658+
or section.startswith("options")
659+
or section in _setuptools_commands()
660+
)
682661

683662
# FIXME: 'Distribution._set_command_options' is too complex (14)
684663
def _set_command_options(self, command_obj, option_dict=None): # noqa: C901
@@ -1105,6 +1084,18 @@ def run_command(self, command) -> None:
11051084
super().run_command(command)
11061085

11071086

1087+
@functools.cache
1088+
def _setuptools_commands() -> set[str]:
1089+
try:
1090+
# Use older API for importlib.metadata compatibility
1091+
entry_points = metadata.distribution('setuptools').entry_points
1092+
eps: Iterable[str] = (ep.name for ep in entry_points)
1093+
except metadata.PackageNotFoundError:
1094+
# during bootstrapping, distribution doesn't exist
1095+
eps = []
1096+
return {*distutils.command.__all__, *eps}
1097+
1098+
11081099
class DistDeprecationWarning(SetuptoolsDeprecationWarning):
11091100
"""Class for warning about deprecations in dist in
11101101
setuptools. Not ignored by default, unlike DeprecationWarning."""

setuptools/tests/config/test_setupcfg.py

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import configparser
22
import contextlib
33
import inspect
4+
import re
45
from pathlib import Path
56
from unittest.mock import Mock, patch
67

@@ -9,6 +10,7 @@
910

1011
from setuptools.config.setupcfg import ConfigHandler, Target, read_configuration
1112
from setuptools.dist import Distribution, _Distribution
13+
from setuptools.errors import InvalidConfigError
1214
from setuptools.warnings import SetuptoolsDeprecationWarning
1315

1416
from ..textwrap import DALS
@@ -420,36 +422,36 @@ def test_not_utf8(self, tmpdir):
420422
with get_dist(tmpdir):
421423
pass
422424

423-
@pytest.mark.xfail(reason="#4864")
424-
def test_warn_dash_deprecation(self, tmpdir):
425-
# warn_dash_deprecation() is a method in setuptools.dist
426-
# remove this test and the method when no longer needed
427-
fake_env(
428-
tmpdir,
429-
'[metadata]\n'
430-
'author-email = [email protected]\n'
431-
'maintainer_email = foo@foo.com\n',
432-
)
433-
msg = "Usage of dash-separated 'author-email' will not be supported"
434-
with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
435-
with get_dist(tmpdir) as dist:
436-
metadata = dist.metadata
437-
438-
assert metadata.author_email == '[email protected]'
439-
assert metadata.maintainer_email == '[email protected]'
440-
441-
@pytest.mark.xfail(reason="#4864")
442-
def test_make_option_lowercase(self, tmpdir):
443-
# remove this test and the method make_option_lowercase() in setuptools.dist
444-
# when no longer needed
445-
fake_env(tmpdir, '[metadata]\nName = foo\ndescription = Some description\n')
446-
msg = "Usage of uppercase key 'Name' in 'metadata' will not be supported"
447-
with pytest.warns(SetuptoolsDeprecationWarning, match=msg):
448-
with get_dist(tmpdir) as dist:
449-
metadata = dist.metadata
450-
451-
assert metadata.name == 'foo'
452-
assert metadata.description == 'Some description'
425+
@pytest.mark.parametrize(
426+
("error_msg", "config"),
427+
[
428+
(
429+
"Invalid dash-separated key 'author-email' in 'metadata' (setup.cfg)",
430+
DALS(
431+
"""
432+
[metadata]
433+
author-email = test@test.com
434+
maintainer_email = [email protected]
435+
"""
436+
),
437+
),
438+
(
439+
"Invalid uppercase key 'Name' in 'metadata' (setup.cfg)",
440+
DALS(
441+
"""
442+
[metadata]
443+
Name = foo
444+
description = Some description
445+
"""
446+
),
447+
),
448+
],
449+
)
450+
def test_invalid_options_previously_deprecated(self, tmpdir, error_msg, config):
451+
# this test and related methods can be removed when no longer needed
452+
fake_env(tmpdir, config)
453+
with pytest.raises(InvalidConfigError, match=re.escape(error_msg)):
454+
get_dist(tmpdir).__enter__()
453455

454456

455457
class TestOptions:

0 commit comments

Comments
 (0)