Skip to content

Commit b9d3f99

Browse files
committed
Facilitate _validate_pyproject for re-packagers (#3229)
2 parents 0ec53b2 + 207354b commit b9d3f99

19 files changed

+64
-55
lines changed

.coveragerc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
omit =
33
# leading `*/` for pytest-dev/pytest-cov#456
44
*/.tox/*
5+
*/_validate_pyproject/* # generated code, tested in `validate-pyproject`
56

67
[report]
78
show_missing = True

.flake8

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ extend-exclude =
88
build
99
setuptools/_vendor
1010
setuptools/_distutils
11+
setuptools/config/_validate_pyproject/fastjsonschema_*
1112
pkg_resources/_vendor
1213

1314
extend-ignore =

changelog.d/3229.change.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Disabled automatic download of ``trove-classifiers`` to facilitate reproducibility.

changelog.d/3229.misc.1.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Updated ``pyproject.toml`` validation via ``validate-pyproject`` v0.7.1.

changelog.d/3229.misc.2.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
New internal tool made available for updating the code responsible for
2+
the validation of ``pyproject.toml``.
3+
This tool can be executed via ``tox -e generate-validation-code``.

conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ def pytest_configure(config):
3232
'pkg_resources/tests/data',
3333
'setuptools/_vendor',
3434
'pkg_resources/_vendor',
35+
'setuptools/config/_validate_pyproject',
3536
]
3637

3738

setuptools/_vendor/vendored.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,3 @@ typing_extensions==4.0.1
1111
# required for importlib_resources and _metadata on older Pythons
1212
zipp==3.7.0
1313
tomli==2.0.1
14-
# validate-pyproject[all]==0.6.1 # Special handling in tools/vendored, don't uncomment or remove

setuptools/_vendor/_validate_pyproject/NOTICE renamed to setuptools/config/_validate_pyproject/NOTICE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
The code contained in this directory was automatically generated using the
22
following command:
33

4-
python -m validate_pyproject.vendoring --output-dir=setuptools/_vendor/_validate_pyproject --enable-plugins setuptools distutils --very-verbose
4+
python -m validate_pyproject.pre_compile --output-dir=setuptools/config/_validate_pyproject --enable-plugins setuptools distutils --very-verbose
55

66
Please avoid changing it manually.
77

setuptools/_vendor/_validate_pyproject/error_reporting.py renamed to setuptools/config/_validate_pyproject/error_reporting.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,6 @@ def _child_prefix(self, parent_prefix: str, child_prefix: str) -> str:
313313
def _separate_terms(word: str) -> List[str]:
314314
"""
315315
>>> _separate_terms("FooBar-foo")
316-
"foo bar foo"
316+
['foo', 'bar', 'foo']
317317
"""
318318
return [w.lower() for w in _CAMEL_CASE_SPLITTER.split(word) if w]

setuptools/_vendor/_validate_pyproject/formats.py renamed to setuptools/config/_validate_pyproject/formats.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,20 @@ class _TroveClassifier:
131131

132132
def __init__(self):
133133
self.downloaded: typing.Union[None, False, typing.Set[str]] = None
134+
self._skip_download = False
134135
# None => not cached yet
135136
# False => cache not available
136137
self.__name__ = "trove_classifier" # Emulate a public function
137138

139+
def _disable_download(self):
140+
# This is a private API. Only setuptools has the consent of using it.
141+
self._skip_download = True
142+
138143
def __call__(self, value: str) -> bool:
139-
if self.downloaded is False:
144+
if self.downloaded is False or self._skip_download is True:
140145
return True
141146

142-
if os.getenv("NO_NETWORK"):
147+
if os.getenv("NO_NETWORK") or os.getenv("VALIDATE_PYPROJECT_NO_NETWORK"):
143148
self.downloaded = False
144149
msg = (
145150
"Install ``trove-classifiers`` to ensure proper validation. "

setuptools/config/pyprojecttoml.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,17 @@ def load_file(filepath: _Path) -> dict:
2626
return tomli.load(file)
2727

2828

29-
def validate(config: dict, filepath: _Path):
30-
from setuptools.extern._validate_pyproject import validate as _validate
29+
def validate(config: dict, filepath: _Path) -> bool:
30+
from . import _validate_pyproject as validator
3131

32-
try:
33-
return _validate(config)
34-
except Exception as ex:
35-
if ex.__class__.__name__ != "ValidationError":
36-
# Workaround for the fact that `extern` can duplicate imports
37-
ex_cls = ex.__class__.__name__
38-
error = ValueError(f"invalid pyproject.toml config: {ex_cls} - {ex}")
39-
raise error from None
32+
trove_classifier = validator.FORMAT_FUNCTIONS.get("trove-classifier")
33+
if hasattr(trove_classifier, "_disable_download"):
34+
# Improve reproducibility by default. See issue 31 for validate-pyproject.
35+
trove_classifier._disable_download() # type: ignore
4036

37+
try:
38+
return validator.validate(config)
39+
except validator.ValidationError as ex:
4140
_logger.error(f"configuration error: {ex.summary}") # type: ignore
4241
_logger.debug(ex.details) # type: ignore
4342
error = ValueError(f"invalid pyproject.toml config: {ex.name}") # type: ignore

setuptools/extern/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ def install(self):
7171

7272
names = (
7373
'packaging', 'pyparsing', 'ordered_set', 'more_itertools', 'importlib_metadata',
74-
'zipp', 'importlib_resources', 'jaraco', 'typing_extensions', 'nspektr',
75-
'tomli', '_validate_pyproject',
74+
'zipp', 'importlib_resources', 'jaraco', 'typing_extensions', 'nspektr', 'tomli',
7675
)
7776
VendorImporter(__name__, names, 'setuptools._vendor').install()

tools/generate_validation_code.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import subprocess
2+
import sys
3+
4+
from pathlib import Path
5+
6+
7+
def generate_pyproject_validation(dest: Path):
8+
"""
9+
Generates validation code for ``pyproject.toml`` based on JSON schemas and the
10+
``validate-pyproject`` library.
11+
"""
12+
cmd = [
13+
sys.executable,
14+
"-m",
15+
"validate_pyproject.vendoring",
16+
f"--output-dir={dest}",
17+
"--enable-plugins",
18+
"setuptools",
19+
"distutils",
20+
"--very-verbose"
21+
]
22+
subprocess.check_call(cmd)
23+
print(f"Validation code generated at: {dest}")
24+
25+
26+
def main():
27+
generate_pyproject_validation(Path("setuptools/config/_validate_pyproject"))
28+
29+
30+
__name__ == '__main__' and main()

tools/vendored.py

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import re
22
import sys
3-
import string
43
import subprocess
5-
import venv
6-
from tempfile import TemporaryDirectory
74

85
from path import Path
96

@@ -140,7 +137,6 @@ def update_pkg_resources():
140137
def update_setuptools():
141138
vendor = Path('setuptools/_vendor')
142139
install(vendor)
143-
install_validate_pyproject(vendor)
144140
rewrite_packaging(vendor / 'packaging', 'setuptools.extern')
145141
rewrite_jaraco_text(vendor / 'jaraco/text', 'setuptools.extern')
146142
rewrite_jaraco(vendor / 'jaraco', 'setuptools.extern')
@@ -150,38 +146,4 @@ def update_setuptools():
150146
rewrite_nspektr(vendor / "nspektr", 'setuptools.extern')
151147

152148

153-
def install_validate_pyproject(vendor):
154-
"""``validate-pyproject`` can be vendorized to remove all dependencies"""
155-
req = next(
156-
(x for x in (vendor / "vendored.txt").lines() if 'validate-pyproject' in x),
157-
"validate-pyproject[all]"
158-
)
159-
160-
pkg, _, _ = req.strip(string.whitespace + "#").partition("#")
161-
pkg = pkg.strip()
162-
163-
opts = {}
164-
if sys.version_info[:2] >= (3, 10):
165-
opts["ignore_cleanup_errors"] = True
166-
167-
with TemporaryDirectory(**opts) as tmp:
168-
env_builder = venv.EnvBuilder(with_pip=True)
169-
env_builder.create(tmp)
170-
context = env_builder.ensure_directories(tmp)
171-
venv_python = getattr(context, 'env_exec_cmd', context.env_exe)
172-
173-
subprocess.check_call([venv_python, "-m", "pip", "install", pkg])
174-
cmd = [
175-
venv_python,
176-
"-m",
177-
"validate_pyproject.vendoring",
178-
f"--output-dir={vendor / '_validate_pyproject' !s}",
179-
"--enable-plugins",
180-
"setuptools",
181-
"distutils",
182-
"--very-verbose"
183-
]
184-
subprocess.check_call(cmd)
185-
186-
187149
__name__ == '__main__' and update_vendored()

tox.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ deps =
6565
commands =
6666
python -m tools.vendored
6767

68+
[testenv:generate-validation-code]
69+
skip_install = True
70+
deps =
71+
validate-pyproject[all]==0.7.1
72+
commands =
73+
python -m tools.generate_validation_code
74+
6875
[testenv:release]
6976
skip_install = True
7077
deps =

0 commit comments

Comments
 (0)