Skip to content

Commit cfbbe2c

Browse files
A5rocksTeamSpen210pre-commit-ci[bot]
authored
Give Pyright what it wants (alias attributes everywhere) (#3114)
* Pyright wants aliases everywhere * Changelog * Make backslash replacement more correct * Debug which class doesn't exist * Debug a bit better * Fix weirdness around PosixPath needing to be resolved * Appease type checker * Don't even consider tests in the alias test * Turn paths into strings before indexing * Explain the test better * Fixes for pre-commit * Reformat according to black * One last pre-commit fix * Simplify alias test by monkeypatching attrs. * Skip pyright init attributes test if plugin has not run * Catch any other renaming too * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Handle autoattribs * Disambiguate * Synchronise name * Update import mode because it worked locally * Check suspicions * Double check * Don't rely on installing `_trio_check_attrs_aliases.py` as a module * importlib import mode means we can use conftest again! * Just do something hacky instead * Debug and remove `-Wall` for later PR * Hopefully fix some things * Give into perl * Add perl to alpine (should work now) --------- Co-authored-by: Spencer Brown <[email protected]> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent f9411f4 commit cfbbe2c

File tree

9 files changed

+81
-14
lines changed

9 files changed

+81
-14
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ jobs:
189189
# can't use setup-python because that python doesn't seem to work;
190190
# `python3-dev` (rather than `python:alpine`) for some ctypes reason,
191191
# `nodejs` for pyright (`node-env` pulls in nodejs but that takes a while and can time out the test).
192-
run: apk update && apk add python3-dev bash nodejs
192+
# `perl` for a platform independent `sed -i` alternative
193+
run: apk update && apk add python3-dev bash nodejs perl
193194
- name: Enter virtual environment
194195
run: python -m venv .venv
195196
- name: Run tests

ci.sh

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,23 +116,29 @@ else
116116
echo "::group::Setup for tests"
117117

118118
# We run the tests from inside an empty directory, to make sure Python
119-
# doesn't pick up any .py files from our working dir. Might have been
120-
# pre-created by some of the code above.
119+
# doesn't pick up any .py files from our working dir. Might have already
120+
# been created by a previous run.
121121
mkdir empty || true
122122
cd empty
123123

124124
INSTALLDIR=$(python -c "import os, trio; print(os.path.dirname(trio.__file__))")
125-
cp ../pyproject.toml "$INSTALLDIR"
125+
cp ../pyproject.toml "$INSTALLDIR" # TODO: remove this
126126

127127
# get mypy tests a nice cache
128128
MYPYPATH=".." mypy --config-file= --cache-dir=./.mypy_cache -c "import trio" >/dev/null 2>/dev/null || true
129129

130130
# support subprocess spawning with coverage.py
131131
echo "import coverage; coverage.process_startup()" | tee -a "$INSTALLDIR/../sitecustomize.py"
132132

133+
perl -i -pe 's/-p trio\._tests\.pytest_plugin//' "$INSTALLDIR/pyproject.toml"
134+
133135
echo "::endgroup::"
134136
echo "::group:: Run Tests"
135-
if COVERAGE_PROCESS_START=$(pwd)/../pyproject.toml coverage run --rcfile=../pyproject.toml -m pytest -ra --junitxml=../test-results.xml --run-slow "${INSTALLDIR}" --verbose --durations=10 $flags; then
137+
if PYTHONPATH=../tests COVERAGE_PROCESS_START=$(pwd)/../pyproject.toml \
138+
coverage run --rcfile=../pyproject.toml -m \
139+
pytest -ra --junitxml=../test-results.xml \
140+
-p _trio_check_attrs_aliases --verbose --durations=10 \
141+
-p trio._tests.pytest_plugin --run-slow $flags "${INSTALLDIR}"; then
136142
PASSED=true
137143
else
138144
PASSED=false

newsfragments/3114.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure that Pyright recognizes our underscore prefixed attributes for attrs classes.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ reportUnnecessaryTypeIgnoreComment = true
203203
typeCheckingMode = "strict"
204204

205205
[tool.pytest.ini_options]
206-
addopts = ["--strict-markers", "--strict-config", "-p trio._tests.pytest_plugin"]
206+
addopts = ["--strict-markers", "--strict-config", "-p trio._tests.pytest_plugin", "--import-mode=importlib"]
207207
faulthandler_timeout = 60
208208
filterwarnings = [
209209
"error",

src/trio/_core/_local.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class RunVar(Generic[T]):
3838
3939
"""
4040

41-
_name: str
42-
_default: T | type[_NoValue] = _NoValue
41+
_name: str = attrs.field(alias="name")
42+
_default: T | type[_NoValue] = attrs.field(default=_NoValue, alias="default")
4343

4444
def get(self, default: T | type[_NoValue] = _NoValue) -> T:
4545
"""Gets the value of this :class:`RunVar` for the current run call."""

src/trio/_core/_run.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -543,9 +543,13 @@ class CancelScope:
543543
cancelled_caught: bool = attrs.field(default=False, init=False)
544544

545545
# Constructor arguments:
546-
_relative_deadline: float = attrs.field(default=inf, kw_only=True)
547-
_deadline: float = attrs.field(default=inf, kw_only=True)
548-
_shield: bool = attrs.field(default=False, kw_only=True)
546+
_relative_deadline: float = attrs.field(
547+
default=inf,
548+
kw_only=True,
549+
alias="relative_deadline",
550+
)
551+
_deadline: float = attrs.field(default=inf, kw_only=True, alias="deadline")
552+
_shield: bool = attrs.field(default=False, kw_only=True, alias="shield")
549553

550554
def __attrs_post_init__(self) -> None:
551555
if isnan(self._deadline):

src/trio/_core/_tests/tutil.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
import pytest
1414

15-
# See trio/_tests/conftest.py for the other half of this
15+
# See trio/_tests/pytest_plugin.py for the other half of this
1616
from trio._tests.pytest_plugin import RUN_SLOW
1717

1818
if TYPE_CHECKING:

src/trio/_tests/test_exports.py

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919

2020
import trio
2121
import trio.testing
22-
from trio._tests.pytest_plugin import skip_if_optional_else_raise
22+
from trio._tests.pytest_plugin import RUN_SLOW, skip_if_optional_else_raise
2323

2424
from .. import _core, _util
2525
from .._core._tests.tutil import slow
26-
from .pytest_plugin import RUN_SLOW
2726

2827
if TYPE_CHECKING:
2928
from collections.abc import Iterable, Iterator
@@ -574,3 +573,37 @@ def test_classes_are_final() -> None:
574573
continue
575574

576575
assert class_is_final(class_)
576+
577+
578+
# Plugin might not be running, especially if running from an installed version.
579+
@pytest.mark.skipif(
580+
not hasattr(attrs.field, "trio_modded"),
581+
reason="Pytest plugin not installed.",
582+
)
583+
def test_pyright_recognizes_init_attributes() -> None:
584+
"""Check whether we provide `alias` for all underscore prefixed attributes.
585+
586+
Attrs always sets the `alias` attribute on fields, so a pytest plugin is used
587+
to monkeypatch `field()` to record whether an alias was defined in the metadata.
588+
See `_trio_check_attrs_aliases`.
589+
"""
590+
for module in PUBLIC_MODULES:
591+
for class_ in module.__dict__.values():
592+
if not attrs.has(class_):
593+
continue
594+
if isinstance(class_, _util.NoPublicConstructor):
595+
continue
596+
597+
attributes = [
598+
attr
599+
for attr in attrs.fields(class_)
600+
if attr.init
601+
if attr.alias
602+
not in (
603+
attr.name,
604+
# trio_original_args may not be present in autoattribs
605+
attr.metadata.get("trio_original_args", {}).get("alias"),
606+
)
607+
]
608+
609+
assert attributes == [], class_

tests/_trio_check_attrs_aliases.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""Plugins are executed by Pytest before test modules.
2+
3+
We use this to monkeypatch attrs.field(), so that we can detect if aliases are used for test_exports.
4+
"""
5+
6+
from typing import Any
7+
8+
import attrs
9+
10+
orig_field = attrs.field
11+
12+
13+
def field(**kwargs: Any) -> Any:
14+
original_args = kwargs.copy()
15+
metadata = kwargs.setdefault("metadata", {})
16+
metadata["trio_original_args"] = original_args
17+
return orig_field(**kwargs)
18+
19+
20+
# Mark it as being ours, so the test knows it can actually run.
21+
field.trio_modded = True # type: ignore
22+
attrs.field = field

0 commit comments

Comments
 (0)