Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions newsfragments/4696.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Fix clashes for ``optional-dependencies`` in ``pyproject.toml`` and
``extra_requires`` in ``setup.cfg/setup.py``.
As per PEP 621, ``optional-dependencies`` has to be honoured and dynamic
behaviour is not allowed.
6 changes: 4 additions & 2 deletions setuptools/config/_apply_pyprojecttoml.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,10 @@ def _dependencies(dist: Distribution, val: list, _root_dir):


def _optional_dependencies(dist: Distribution, val: dict, _root_dir):
existing = getattr(dist, "extras_require", None) or {}
dist.extras_require = {**existing, **val}
if getattr(dist, "extras_require", None):
msg = "`extras_require` overwritten in `pyproject.toml` (optional-dependencies)"
SetuptoolsWarning.emit(msg)
dist.extras_require = val


def _ext_modules(dist: Distribution, val: list[dict]) -> list[Extension]:
Expand Down
32 changes: 21 additions & 11 deletions setuptools/tests/config/test_pyprojecttoml_dynamic_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from setuptools.config.pyprojecttoml import apply_configuration
from setuptools.dist import Distribution
from setuptools.warnings import SetuptoolsWarning


def test_dynamic_dependencies(tmp_path):
Expand Down Expand Up @@ -77,23 +78,32 @@ def test_mixed_dynamic_optional_dependencies(tmp_path):

[tool.setuptools.dynamic.optional-dependencies.images]
file = ["requirements-images.txt"]

[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
"""
),
}

path.build(files, prefix=tmp_path)

# Test that the mix-and-match doesn't currently validate.
pyproject = tmp_path / "pyproject.toml"
with pytest.raises(ValueError, match="project.optional-dependencies"):
apply_configuration(Distribution(), pyproject)

# Explicitly disable the validation and try again, to see that the mix-and-match
# result would be correct.
dist = Distribution()
dist = apply_configuration(dist, pyproject, ignore_option_errors=True)
assert dist.extras_require == {"docs": ["sphinx"], "images": ["pillow~=42.0"]}

def test_mixed_extras_require_optional_dependencies(tmp_path):
files = {
"pyproject.toml": cleandoc(
"""
[project]
name = "myproj"
version = "1.0"
optional-dependencies.docs = ["sphinx"]
"""
),
}

path.build(files, prefix=tmp_path)
pyproject = tmp_path / "pyproject.toml"

with pytest.warns(SetuptoolsWarning, match=".extras_require. overwritten"):
dist = Distribution({"extras_require": {"hello": ["world"]}})
dist = apply_configuration(dist, pyproject)
assert dist.extras_require == {"docs": ["sphinx"]}