Skip to content

Commit

Permalink
Use dependency groups & a fully locked dev environment
Browse files Browse the repository at this point in the history
  • Loading branch information
hynek committed Jan 24, 2025
1 parent c17b1f3 commit 6234f14
Show file tree
Hide file tree
Showing 5 changed files with 2,388 additions and 47 deletions.
46 changes: 26 additions & 20 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ name = "attrs"
authors = [{ name = "Hynek Schlawack", email = "[email protected]" }]
license = "MIT"
license-files = ["LICENSE"]
requires-python = ">=3.8"
requires-python = ">=3.9"
description = "Classes Without Boilerplate"
keywords = ["class", "attribute", "boilerplate"]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
Expand All @@ -28,29 +27,41 @@ classifiers = [
dependencies = []
dynamic = ["version", "readme"]

[project.optional-dependencies]
[project.urls]
Documentation = "https://www.attrs.org/"
Changelog = "https://www.attrs.org/en/stable/changelog.html"
GitHub = "https://github.com/python-attrs/attrs"
Funding = "https://github.com/sponsors/hynek"
Tidelift = "https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi"


[dependency-groups]
tests-mypy = [
'pytest-mypy-plugins; platform_python_implementation == "CPython" and python_version >= "3.10"',
# Since the mypy error messages keep changing, we have to keep updating this
# pin.
'mypy>=1.11.1; platform_python_implementation == "CPython" and python_version >= "3.10"',
]
tests = [
{ include-group = "tests-mypy" },
# For regression test to ensure cloudpickle compat doesn't break.
'cloudpickle; platform_python_implementation == "CPython"',
"hypothesis",
"pympler",
# 4.3.0 dropped last use of `convert`
"pytest>=4.3.0",
"pytest-xdist[psutil]",
"attrs[tests-mypy]",
]
cov = [
"attrs[tests]",
{ include-group = "tests" },
# Ensure coverage is new enough for `source_pkgs`.
"coverage[toml]>=5.3",
]
benchmark = ["pytest-codspeed", "pytest-xdist[psutil]", "attrs[tests]"]
pyright = ["pyright", { include-group = "tests" }]
benchmark = [
{ include-group = "tests" },
"pytest-codspeed",
"pytest-xdist[psutil]",
]
docs = [
"cogapp",
"furo",
Expand All @@ -59,17 +70,10 @@ docs = [
"sphinx-notfound-page",
"sphinxcontrib-towncrier",
# See https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92
# Pin also present in tox.ini
"towncrier<24.7",
]
dev = ["attrs[tests]", "pre-commit-uv"]

[project.urls]
Documentation = "https://www.attrs.org/"
Changelog = "https://www.attrs.org/en/stable/changelog.html"
GitHub = "https://github.com/python-attrs/attrs"
Funding = "https://github.com/sponsors/hynek"
Tidelift = "https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi"
docs-watch = [{ include-group = "docs" }, "watchfiles"]
dev = [{ include-group = "tests" }]


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

[tool.ruff.lint.per-file-ignores]
Expand Down Expand Up @@ -254,10 +260,10 @@ ignore = [
"src/*/*.pyi" = ["ALL"] # TODO
"tests/test_annotations.py" = ["FA100"]
"tests/typing_example.py" = [
"E741", # ambiguous variable names don't matter in type checks
"B018", # useless expressions aren't useless in type checks
"B015", # pointless comparison in type checks aren't pointless
"UP037", # we test some older syntaxes on purpose
"E741", # ambiguous variable names don't matter in type checks
"B018", # useless expressions aren't useless in type checks
"B015", # pointless comparison in type checks aren't pointless
"UP037", # we test some older syntaxes on purpose
]

[tool.ruff.lint.isort]
Expand Down
1 change: 0 additions & 1 deletion src/attr/_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@


PYPY = platform.python_implementation() == "PyPy"
PY_3_9_PLUS = sys.version_info[:2] >= (3, 9)
PY_3_10_PLUS = sys.version_info[:2] >= (3, 10)
PY_3_11_PLUS = sys.version_info[:2] >= (3, 11)
PY_3_12_PLUS = sys.version_info[:2] >= (3, 12)
Expand Down
11 changes: 6 additions & 5 deletions src/attr/_funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import copy

from ._compat import PY_3_9_PLUS, get_generic_base
from ._compat import get_generic_base
from ._make import _OBJ_SETATTR, NOTHING, fields
from .exceptions import AttrsAttributeNotFoundError

Expand Down Expand Up @@ -450,10 +450,11 @@ class yet.
if getattr(cls, "__attrs_types_resolved__", None) != cls:
import typing

kwargs = {"globalns": globalns, "localns": localns}

if PY_3_9_PLUS:
kwargs["include_extras"] = include_extras
kwargs = {
"globalns": globalns,
"localns": localns,
"include_extras": include_extras,
}

hints = typing.get_type_hints(cls, **kwargs)
for field in fields(cls) if attribs is None else attribs:
Expand Down
46 changes: 25 additions & 21 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
min_version = 4
env_list =
pre-commit,
py3{8,9,10,11,12,13}-tests,
py3{9,10,11,12,13}-tests,
py3{10,11,12,13}-mypy,
pypy3-tests,
pyright,
Expand All @@ -16,45 +16,46 @@ pass_env = SETUPTOOLS_SCM_PRETEND_VERSION


[testenv]
runner = uv-venv-lock-runner
package = wheel
wheel_build_env = .pkg
extras =
dependency_groups =
tests: tests
mypy: tests-mypy
commands =
tests: pytest {posargs:-n auto}
tests: pytest {posargs}
mypy: mypy tests/typing_example.py
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

[testenv:pypy3-tests]
extras = tests
dependency_groups = tests
commands = pytest tests/test_functional.py

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

[testenv:coverage-report]
# Keep base_python in-sync with .python-version-default
base_python = py313
# Keep depends in-sync with testenv above that has cov extra.
depends = py3{8,10,13}-tests
# Keep depends in-sync with testenv above that has the cov dependency group.
depends = py3{9,10,13}-tests
skip_install = true
deps = coverage[toml]>=5.3
dependency_groups = cov
commands =
coverage combine
coverage report


[testenv:codspeed]
extras = benchmark
dependency_groups = benchmark
pass_env =
CODSPEED_TOKEN
CODSPEED_ENV
Expand All @@ -65,9 +66,10 @@ commands = pytest --codspeed -n auto bench/test_benchmarks.py


[testenv:docs-{build,doctests,linkcheck}]
# Keep base_python in sync with ci.yml/docs and .readthedocs.yaml.
runner = uv-venv-lock-runner
# Keep base_python in-sync with ci.yml/docs and .readthedocs.yaml.
base_python = py313
extras = docs
dependency_groups = docs
commands =
build: sphinx-build -n -T -W -b html -d {envtmpdir}/doctrees docs {posargs:docs/_build/}html
doctests: sphinx-build -n -T -W -b doctest -d {envtmpdir}/doctrees docs {posargs:docs/_build/}html
Expand All @@ -76,46 +78,48 @@ commands =
[testenv:docs-watch]
package = editable
base_python = {[testenv:docs-build]base_python}
extras = {[testenv:docs-build]extras}
dependency_groups = docs-watch
deps = watchfiles
commands =
watchfiles \
--ignore-paths docs/_build/ \
'sphinx-build -W -n --jobs auto -b html -d {envtmpdir}/doctrees docs docs/_build/html' \
README.md \
src \
docs

[testenv:docs-sponsors]
runner = uv-venv-runner
skip_install = true
description = Ensure sponsor logos are up to date.
deps = cogapp
commands = cog -rP README.md docs/index.md


[testenv:pre-commit]
runner = uv-venv-runner
skip_install = true
deps = pre-commit-uv
commands = pre-commit run --all-files


[testenv:changelog]
# See https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92
# Pin also present in pyproject.toml
deps = towncrier<24.7
dependency_groups = docs
skip_install = true
commands =
towncrier --version
towncrier build --version main --draft


[testenv:pyright]
extras = tests
deps = pyright>=1.1.380
dependency_groups = pyright
commands = pytest tests/test_pyright.py -vv


[testenv:docset]
runner = uv-venv-runner
deps = doc2dash
extras = docs
dependency_groups = docs
allowlist_externals =
rm
cp
Expand Down
Loading

0 comments on commit 6234f14

Please sign in to comment.