Skip to content

Commit d92218e

Browse files
[PEP 771]
1 parent fb7f3d3 commit d92218e

14 files changed

+548
-146
lines changed

.gitignore

+171
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Byte-compiled / optimized / DLL files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# C extensions
7+
*.so
8+
9+
# Distribution / packaging
10+
.Python
11+
build/
12+
develop-eggs/
13+
dist/
14+
downloads/
15+
eggs/
16+
.eggs/
17+
lib/
18+
lib64/
19+
parts/
20+
sdist/
21+
var/
22+
wheels/
23+
share/python-wheels/
24+
*.egg-info/
25+
.installed.cfg
26+
*.egg
27+
MANIFEST
28+
29+
# PyInstaller
30+
# Usually these files are written by a python script from a template
31+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
32+
*.manifest
33+
*.spec
34+
35+
# Installer logs
36+
pip-log.txt
37+
pip-delete-this-directory.txt
38+
39+
# Unit test / coverage reports
40+
htmlcov/
41+
.tox/
42+
.nox/
43+
.coverage
44+
.coverage.*
45+
.cache
46+
nosetests.xml
47+
coverage.xml
48+
*.cover
49+
*.py,cover
50+
.hypothesis/
51+
.pytest_cache/
52+
cover/
53+
54+
# Translations
55+
*.mo
56+
*.pot
57+
58+
# Django stuff:
59+
*.log
60+
local_settings.py
61+
db.sqlite3
62+
db.sqlite3-journal
63+
64+
# Flask stuff:
65+
instance/
66+
.webassets-cache
67+
68+
# Scrapy stuff:
69+
.scrapy
70+
71+
# Sphinx documentation
72+
docs/_build/
73+
74+
# PyBuilder
75+
.pybuilder/
76+
target/
77+
78+
# Jupyter Notebook
79+
.ipynb_checkpoints
80+
81+
# IPython
82+
profile_default/
83+
ipython_config.py
84+
85+
# pyenv
86+
# For a library or package, you might want to ignore these files since the code is
87+
# intended to run in multiple environments; otherwise, check them in:
88+
# .python-version
89+
90+
# pipenv
91+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
93+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
94+
# install all needed dependencies.
95+
#Pipfile.lock
96+
97+
# UV
98+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99+
# This is especially recommended for binary packages to ensure reproducibility, and is more
100+
# commonly ignored for libraries.
101+
#uv.lock
102+
103+
# poetry
104+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105+
# This is especially recommended for binary packages to ensure reproducibility, and is more
106+
# commonly ignored for libraries.
107+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108+
#poetry.lock
109+
110+
# pdm
111+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112+
#pdm.lock
113+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114+
# in version control.
115+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
116+
.pdm.toml
117+
.pdm-python
118+
.pdm-build/
119+
120+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
121+
__pypackages__/
122+
123+
# Celery stuff
124+
celerybeat-schedule
125+
celerybeat.pid
126+
127+
# SageMath parsed files
128+
*.sage.py
129+
130+
# Environments
131+
.env
132+
.venv
133+
env/
134+
venv/
135+
ENV/
136+
env.bak/
137+
venv.bak/
138+
139+
# Spyder project settings
140+
.spyderproject
141+
.spyproject
142+
143+
# Rope project settings
144+
.ropeproject
145+
146+
# mkdocs documentation
147+
/site
148+
149+
# mypy
150+
.mypy_cache/
151+
.dmypy.json
152+
dmypy.json
153+
154+
# Pyre type checker
155+
.pyre/
156+
157+
# pytype static type analyzer
158+
.pytype/
159+
160+
# Cython debug symbols
161+
cython_debug/
162+
163+
# PyCharm
164+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
165+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
166+
# and can be added to the global gitignore or merged into this file. For a more nuclear
167+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
168+
#.idea/
169+
170+
# PyPI configuration file
171+
.pypirc

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ keywords = "setuptools.dist:Distribution._finalize_setup_keywords"
174174
eager_resources = "setuptools.dist:assert_string_list"
175175
namespace_packages = "setuptools.dist:check_nsp"
176176
extras_require = "setuptools.dist:check_extras"
177+
default_extras_require = "setuptools.dist:check_default_extras_require"
177178
install_requires = "setuptools.dist:check_requirements"
178179
setup_requires = "setuptools.dist:check_requirements"
179180
python_requires = "setuptools.dist:check_specifier"

setuptools/_core_metadata.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
def get_metadata_version(self):
2929
mv = getattr(self, 'metadata_version', None)
3030
if mv is None:
31-
mv = Version('2.2')
31+
mv = Version('2.5')
3232
self.metadata_version = mv
3333
return mv
3434

@@ -223,6 +223,9 @@ def _write_requirements(self, file):
223223
for req in _reqs.parse(self.install_requires):
224224
file.write(f"Requires-Dist: {req}\n")
225225

226+
for req in _reqs.parse(self.default_extras_require):
227+
file.write(f"Default-Extra: {req}\n")
228+
226229
processed_extras = {}
227230
for augmented_extra, reqs in self.extras_require.items():
228231
# Historically, setuptools allows "augmented extras": `<extra>:<condition>`
@@ -311,6 +314,7 @@ def _distribution_fullname(name: str, version: str) -> str:
311314
"project-url": "project_urls",
312315
"provides": "provides",
313316
# "provides-dist": "provides_dist", # NOT USED
317+
"default-extra": "default_extras_require",
314318
"provides-extra": "extras_require",
315319
"requires": "requires",
316320
"requires-dist": "install_requires",

setuptools/_importlib.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import sys
22

3-
if sys.version_info < (3, 10):
3+
if sys.version_info < (3, 10) or True:
44
import importlib_metadata as metadata # pragma: no cover
55
else:
66
import importlib.metadata as metadata # noqa: F401

setuptools/_vendor/importlib_metadata/_adapters.py

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class Message(email.message.Message):
1515
'Platform',
1616
'Project-URL',
1717
'Provides-Dist',
18+
'Default-Extra'
1819
'Provides-Extra',
1920
'Requires-Dist',
2021
'Requires-External',

setuptools/_vendor/packaging/metadata.py

+12-2
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ class RawMetadata(TypedDict, total=False):
132132
license_expression: str
133133
license_files: list[str]
134134

135+
# Metadata 2.5 - PEP 771
136+
default_extra: list[str]
137+
135138

136139
_STRING_FIELDS = {
137140
"author",
@@ -253,6 +256,7 @@ def _get_payload(msg: email.message.Message, source: bytes | str) -> str:
253256
"author-email": "author_email",
254257
"classifier": "classifiers",
255258
"description": "description",
259+
"default-extra": "default_extra",
256260
"description-content-type": "description_content_type",
257261
"download-url": "download_url",
258262
"dynamic": "dynamic",
@@ -463,8 +467,8 @@ def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]:
463467

464468

465469
# Keep the two values in sync.
466-
_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4"]
467-
_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4"]
470+
_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"]
471+
_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"]
468472

469473
_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"])
470474

@@ -861,3 +865,9 @@ def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata:
861865
"""``Provides`` (deprecated)"""
862866
obsoletes: _Validator[list[str] | None] = _Validator(added="1.1")
863867
"""``Obsoletes`` (deprecated)"""
868+
# PEP 771 lets us define a default `extras_require` if none is passed by the
869+
# user.
870+
default_extra: _Validator[list[utils.NormalizedName] | None] = _Validator(
871+
added="2.5",
872+
)
873+
""":external:ref:`core-metadata-default-extra`"""

setuptools/_vendor/wheel/metadata.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -142,12 +142,12 @@ def generate_requirements(
142142

143143
def pkginfo_to_metadata(egg_info_path: str, pkginfo_path: str) -> Message:
144144
"""
145-
Convert .egg-info directory with PKG-INFO to the Metadata 2.1 format
145+
Convert .egg-info directory with PKG-INFO to the Metadata 2.5 format
146146
"""
147147
with open(pkginfo_path, encoding="utf-8") as headers:
148148
pkg_info = Parser().parse(headers)
149149

150-
pkg_info.replace_header("Metadata-Version", "2.1")
150+
pkg_info.replace_header("Metadata-Version", "2.5")
151151
# Those will be regenerated from `requires.txt`.
152152
del pkg_info["Provides-Extra"]
153153
del pkg_info["Requires-Dist"]

setuptools/config/_apply_pyprojecttoml.py

+10
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,13 @@ def _dependencies(dist: Distribution, val: list, _root_dir: StrPath | None):
226226
dist.install_requires = val
227227

228228

229+
def _default_optional_dependencies(dist: Distribution, val: dict, _root_dir: StrPath | None):
230+
if getattr(dist, "default_extras_require", None):
231+
msg = "`default_extras_require` overwritten in `pyproject.toml` (default-optional-dependencies)"
232+
SetuptoolsWarning.emit(msg)
233+
dist.default_extras_require = val
234+
235+
229236
def _optional_dependencies(dist: Distribution, val: dict, _root_dir: StrPath | None):
230237
if getattr(dist, "extras_require", None):
231238
msg = "`extras_require` overwritten in `pyproject.toml` (optional-dependencies)"
@@ -396,6 +403,7 @@ def _acessor(obj):
396403
"urls": _project_urls,
397404
"dependencies": _dependencies,
398405
"optional_dependencies": _optional_dependencies,
406+
"default_optional_dependencies": _default_optional_dependencies,
399407
"requires_python": _python_requires,
400408
}
401409

@@ -441,6 +449,7 @@ def _acessor(obj):
441449
"scripts": _get_previous_scripts,
442450
"gui-scripts": _get_previous_gui_scripts,
443451
"dependencies": _attrgetter("install_requires"),
452+
"default-optional-dependencies": _attrgetter("default_extras_require"),
444453
"optional-dependencies": _attrgetter("extras_require"),
445454
}
446455

@@ -458,6 +467,7 @@ def _acessor(obj):
458467
"scripts": _static.EMPTY_DICT,
459468
"gui-scripts": _static.EMPTY_DICT,
460469
"dependencies": _static.EMPTY_LIST,
470+
"default-optional-dependencies": _static.EMPTY_LIST,
461471
"optional-dependencies": _static.EMPTY_DICT,
462472
}
463473

setuptools/config/_validate_pyproject/extra_validations.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ class RedefiningStaticFieldAsDynamic(ValidationError):
2424
)
2525

2626

27+
class IncludedDependencyGroupMustExist(ValidationError):
28+
_DESC = """An included dependency group must exist and must not be cyclic.
29+
"""
30+
__doc__ = _DESC
31+
_URL = "https://peps.python.org/pep-0735/"
32+
33+
2734
def validate_project_dynamic(pyproject: T) -> T:
2835
project_table = pyproject.get("project", {})
2936
dynamic = project_table.get("dynamic", [])
@@ -49,4 +56,27 @@ def validate_project_dynamic(pyproject: T) -> T:
4956
return pyproject
5057

5158

52-
EXTRA_VALIDATIONS = (validate_project_dynamic,)
59+
def validate_include_depenency(pyproject: T) -> T:
60+
dependency_groups = pyproject.get("dependency-groups", {})
61+
for key, value in dependency_groups.items():
62+
for each in value:
63+
if (
64+
isinstance(each, dict)
65+
and (include_group := each.get("include-group"))
66+
and include_group not in dependency_groups
67+
):
68+
raise IncludedDependencyGroupMustExist(
69+
message=f"The included dependency group {include_group} doesn't exist",
70+
value=each,
71+
name=f"data.dependency_groups.{key}",
72+
definition={
73+
"description": cleandoc(IncludedDependencyGroupMustExist._DESC),
74+
"see": IncludedDependencyGroupMustExist._URL,
75+
},
76+
rule="PEP 735",
77+
)
78+
# TODO: check for `include-group` cycles (can be conditional to graphlib)
79+
return pyproject
80+
81+
82+
EXTRA_VALIDATIONS = (validate_project_dynamic, validate_include_depenency)

setuptools/config/_validate_pyproject/fastjsonschema_validations.py

+242-135
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)