Skip to content

Commit 658ab60

Browse files
committed
Use dependency groups & a fully locked dev environment
1 parent 06daa1d commit 658ab60

File tree

5 files changed

+1755
-47
lines changed

5 files changed

+1755
-47
lines changed

pyproject.toml

+26-20
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ name = "attrs"
1010
authors = [{ name = "Hynek Schlawack", email = "hs@ox.cx" }]
1111
license = "MIT"
1212
license-files = ["LICENSE"]
13-
requires-python = ">=3.8"
13+
requires-python = ">=3.9"
1414
description = "Classes Without Boilerplate"
1515
keywords = ["class", "attribute", "boilerplate"]
1616
classifiers = [
1717
"Development Status :: 5 - Production/Stable",
18-
"Programming Language :: Python :: 3.8",
1918
"Programming Language :: Python :: 3.9",
2019
"Programming Language :: Python :: 3.10",
2120
"Programming Language :: Python :: 3.11",
@@ -28,29 +27,41 @@ classifiers = [
2827
dependencies = []
2928
dynamic = ["version", "readme"]
3029

31-
[project.optional-dependencies]
30+
[project.urls]
31+
Documentation = "https://www.attrs.org/"
32+
Changelog = "https://www.attrs.org/en/stable/changelog.html"
33+
GitHub = "https://github.com/python-attrs/attrs"
34+
Funding = "https://github.com/sponsors/hynek"
35+
Tidelift = "https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi"
36+
37+
38+
[dependency-groups]
3239
tests-mypy = [
3340
'pytest-mypy-plugins; platform_python_implementation == "CPython" and python_version >= "3.10"',
3441
# Since the mypy error messages keep changing, we have to keep updating this
3542
# pin.
3643
'mypy>=1.11.1; platform_python_implementation == "CPython" and python_version >= "3.10"',
3744
]
3845
tests = [
46+
{ include-group = "tests-mypy" },
3947
# For regression test to ensure cloudpickle compat doesn't break.
4048
'cloudpickle; platform_python_implementation == "CPython"',
4149
"hypothesis",
4250
"pympler",
4351
# 4.3.0 dropped last use of `convert`
4452
"pytest>=4.3.0",
45-
"pytest-xdist[psutil]",
46-
"attrs[tests-mypy]",
4753
]
4854
cov = [
49-
"attrs[tests]",
55+
{ include-group = "tests" },
5056
# Ensure coverage is new enough for `source_pkgs`.
5157
"coverage[toml]>=5.3",
5258
]
53-
benchmark = ["pytest-codspeed", "pytest-xdist[psutil]", "attrs[tests]"]
59+
pyright = ["pyright", { include-group = "tests" }]
60+
benchmark = [
61+
{ include-group = "tests" },
62+
"pytest-codspeed",
63+
"pytest-xdist[psutil]",
64+
]
5465
docs = [
5566
"cogapp",
5667
"furo",
@@ -59,17 +70,10 @@ docs = [
5970
"sphinx-notfound-page",
6071
"sphinxcontrib-towncrier",
6172
# See https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92
62-
# Pin also present in tox.ini
6373
"towncrier<24.7",
6474
]
65-
dev = ["attrs[tests]", "pre-commit-uv"]
66-
67-
[project.urls]
68-
Documentation = "https://www.attrs.org/"
69-
Changelog = "https://www.attrs.org/en/stable/changelog.html"
70-
GitHub = "https://github.com/python-attrs/attrs"
71-
Funding = "https://github.com/sponsors/hynek"
72-
Tidelift = "https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi"
75+
docs-watch = [{ include-group = "docs" }, "watchfiles"]
76+
dev = [{ include-group = "tests" }]
7377

7478

7579
[tool.hatch.version]
@@ -227,6 +231,8 @@ ignore = [
227231
"TD", # we don't follow other people's todo style
228232
"TRY301", # I'm sorry, but this makes not sense for us.
229233
"UP031", # format() is slow as molasses; % and f'' FTW.
234+
"UP006", # replace Dict etc by dict etc later.
235+
"UP035", # replace Dict etc by dict etc later.
230236
]
231237

232238
[tool.ruff.lint.per-file-ignores]
@@ -254,10 +260,10 @@ ignore = [
254260
"src/*/*.pyi" = ["ALL"] # TODO
255261
"tests/test_annotations.py" = ["FA100"]
256262
"tests/typing_example.py" = [
257-
"E741", # ambiguous variable names don't matter in type checks
258-
"B018", # useless expressions aren't useless in type checks
259-
"B015", # pointless comparison in type checks aren't pointless
260-
"UP037", # we test some older syntaxes on purpose
263+
"E741", # ambiguous variable names don't matter in type checks
264+
"B018", # useless expressions aren't useless in type checks
265+
"B015", # pointless comparison in type checks aren't pointless
266+
"UP037", # we test some older syntaxes on purpose
261267
]
262268

263269
[tool.ruff.lint.isort]

src/attr/_compat.py

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111

1212
PYPY = platform.python_implementation() == "PyPy"
13-
PY_3_9_PLUS = sys.version_info[:2] >= (3, 9)
1413
PY_3_10_PLUS = sys.version_info[:2] >= (3, 10)
1514
PY_3_11_PLUS = sys.version_info[:2] >= (3, 11)
1615
PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)

src/attr/_funcs.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import copy
55

6-
from ._compat import PY_3_9_PLUS, get_generic_base
6+
from ._compat import get_generic_base
77
from ._make import _OBJ_SETATTR, NOTHING, fields
88
from .exceptions import AttrsAttributeNotFoundError
99

@@ -450,10 +450,11 @@ class yet.
450450
if getattr(cls, "__attrs_types_resolved__", None) != cls:
451451
import typing
452452

453-
kwargs = {"globalns": globalns, "localns": localns}
454-
455-
if PY_3_9_PLUS:
456-
kwargs["include_extras"] = include_extras
453+
kwargs = {
454+
"globalns": globalns,
455+
"localns": localns,
456+
"include_extras": include_extras,
457+
}
457458

458459
hints = typing.get_type_hints(cls, **kwargs)
459460
for field in fields(cls) if attribs is None else attribs:

tox.ini

+25-21
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
min_version = 4
33
env_list =
44
pre-commit,
5-
py3{8,9,10,11,12,13}-tests,
5+
py3{9,10,11,12,13}-tests,
66
py3{10,11,12,13}-mypy,
77
pypy3-tests,
88
pyright,
@@ -16,45 +16,46 @@ pass_env = SETUPTOOLS_SCM_PRETEND_VERSION
1616

1717

1818
[testenv]
19+
runner = uv-venv-lock-runner
1920
package = wheel
2021
wheel_build_env = .pkg
21-
extras =
22+
dependency_groups =
2223
tests: tests
2324
mypy: tests-mypy
2425
commands =
25-
tests: pytest {posargs:-n auto}
26+
tests: pytest {posargs}
2627
mypy: mypy tests/typing_example.py
2728
mypy: mypy src/attrs/__init__.pyi src/attr/__init__.pyi src/attr/_typing_compat.pyi src/attr/_version_info.pyi src/attr/converters.pyi src/attr/exceptions.pyi src/attr/filters.pyi src/attr/setters.pyi src/attr/validators.pyi
2829

2930
[testenv:pypy3-tests]
30-
extras = tests
31+
dependency_groups = tests
3132
commands = pytest tests/test_functional.py
3233

33-
[testenv:py3{8,10,13}-tests]
34-
extras = cov
34+
[testenv:py3{9,10,13}-tests]
35+
dependency_groups = cov
3536
# Python 3.6+ has a number of compile-time warnings on invalid string escapes.
3637
# PYTHONWARNINGS=d makes them visible during the tox run.
3738
set_env =
3839
COVERAGE_PROCESS_START={toxinidir}/pyproject.toml
3940
PYTHONWARNINGS=d
4041
commands_pre = python -c 'import pathlib; pathlib.Path("{env_site_packages_dir}/cov.pth").write_text("import coverage; coverage.process_startup()")'
41-
# We group xdist execution by file because otherwise the Mypy tests have race conditions.
42-
commands = coverage run -m pytest {posargs:-n auto --dist loadfile}
42+
commands =
43+
coverage run -m pytest {posargs}
4344

4445
[testenv:coverage-report]
4546
# Keep base_python in-sync with .python-version-default
4647
base_python = py313
47-
# Keep depends in-sync with testenv above that has cov extra.
48-
depends = py3{8,10,13}-tests
48+
# Keep depends in-sync with testenv above that has the cov dependency group.
49+
depends = py3{9,10,13}-tests
4950
skip_install = true
50-
deps = coverage[toml]>=5.3
51+
dependency_groups = cov
5152
commands =
5253
coverage combine
5354
coverage report
5455

5556

5657
[testenv:codspeed]
57-
extras = benchmark
58+
dependency_groups = benchmark
5859
pass_env =
5960
CODSPEED_TOKEN
6061
CODSPEED_ENV
@@ -65,9 +66,10 @@ commands = pytest --codspeed -n auto bench/test_benchmarks.py
6566

6667

6768
[testenv:docs-{build,doctests,linkcheck}]
68-
# Keep base_python in sync with ci.yml/docs and .readthedocs.yaml.
69+
runner = uv-venv-lock-runner
70+
# Keep base_python in-sync with ci.yml/docs and .readthedocs.yaml.
6971
base_python = py313
70-
extras = docs
72+
dependency_groups = docs
7173
commands =
7274
build: sphinx-build -n -T -W -b html -d {envtmpdir}/doctrees docs {posargs:docs/_build/}html
7375
doctests: sphinx-build -n -T -W -b doctest -d {envtmpdir}/doctrees docs {posargs:docs/_build/}html
@@ -76,46 +78,48 @@ commands =
7678
[testenv:docs-watch]
7779
package = editable
7880
base_python = {[testenv:docs-build]base_python}
79-
extras = {[testenv:docs-build]extras}
81+
dependency_groups = docs-watch
8082
deps = watchfiles
8183
commands =
8284
watchfiles \
8385
--ignore-paths docs/_build/ \
8486
'sphinx-build -W -n --jobs auto -b html -d {envtmpdir}/doctrees docs docs/_build/html' \
87+
README.md \
8588
src \
8689
docs
8790

8891
[testenv:docs-sponsors]
92+
runner = uv-venv-runner
93+
skip_install = true
8994
description = Ensure sponsor logos are up to date.
9095
deps = cogapp
9196
commands = cog -rP README.md docs/index.md
9297

9398

9499
[testenv:pre-commit]
100+
runner = uv-venv-runner
95101
skip_install = true
96102
deps = pre-commit-uv
97103
commands = pre-commit run --all-files
98104

99105

100106
[testenv:changelog]
101-
# See https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92
102-
# Pin also present in pyproject.toml
103-
deps = towncrier<24.7
107+
dependency_groups = docs
104108
skip_install = true
105109
commands =
106110
towncrier --version
107111
towncrier build --version main --draft
108112

109113

110114
[testenv:pyright]
111-
extras = tests
112-
deps = pyright>=1.1.380
115+
dependency_groups = pyright
113116
commands = pytest tests/test_pyright.py -vv
114117

115118

116119
[testenv:docset]
120+
runner = uv-venv-runner
117121
deps = doc2dash
118-
extras = docs
122+
dependency_groups = docs
119123
allowlist_externals =
120124
rm
121125
cp

0 commit comments

Comments
 (0)