Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Give Pyright what it wants (alias attributes everywhere) #3114

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions newsfragments/3114.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Ensure that Pyright recognizes our underscore prefixed attributes for attrs classes.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ reportUnnecessaryTypeIgnoreComment = true
typeCheckingMode = "strict"

[tool.pytest.ini_options]
addopts = ["--strict-markers", "--strict-config", "-p trio._tests.pytest_plugin"]
addopts = ["--strict-markers", "--strict-config", "-p _trio_check_attrs_aliases", "-p trio._tests.pytest_plugin"]
faulthandler_timeout = 60
filterwarnings = [
"error",
Expand Down
22 changes: 22 additions & 0 deletions src/_trio_check_attrs_aliases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""Conftest is executed by Pytest before test modules.

We use this to monkeypatch attrs.field(), so that we can detect if aliases are used for test_exports.
"""

from typing import Any

import attrs

orig_field = attrs.field


def field(**kwargs: Any) -> Any:
original_args = kwargs.copy()
metadata = kwargs.setdefault("metadata", {})
metadata["trio_original_args"] = original_args
return orig_field(**kwargs)


# Mark it as being ours, so the test knows it can actually run.
field.trio_modded = True # type: ignore
attrs.field = field
4 changes: 2 additions & 2 deletions src/trio/_core/_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ class RunVar(Generic[T]):

"""

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

def get(self, default: T | type[_NoValue] = _NoValue) -> T:
"""Gets the value of this :class:`RunVar` for the current run call."""
Expand Down
10 changes: 7 additions & 3 deletions src/trio/_core/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -544,9 +544,13 @@ class CancelScope:
cancelled_caught: bool = attrs.field(default=False, init=False)

# Constructor arguments:
_relative_deadline: float = attrs.field(default=inf, kw_only=True)
_deadline: float = attrs.field(default=inf, kw_only=True)
_shield: bool = attrs.field(default=False, kw_only=True)
_relative_deadline: float = attrs.field(
default=inf,
kw_only=True,
alias="relative_deadline",
)
_deadline: float = attrs.field(default=inf, kw_only=True, alias="deadline")
_shield: bool = attrs.field(default=False, kw_only=True, alias="shield")

def __attrs_post_init__(self) -> None:
if isnan(self._deadline):
Expand Down
34 changes: 34 additions & 0 deletions src/trio/_tests/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,3 +572,37 @@ def test_classes_are_final() -> None:
continue

assert class_is_final(class_)


# Plugin might not be running, especially if running from an installed version.
@pytest.mark.skipif(
not hasattr(attrs.field, "trio_modded"),
reason="Pytest plugin not installed.",
)
def test_pyright_recognizes_init_attributes() -> None:
"""Check whether we provide `alias` for all underscore prefixed attributes.

Attrs always sets the `alias` attribute on fields, so a pytest plugin is used
to monkeypatch `field()` to record whether an alias was defined in the metadata.
See `_trio_check_attrs_aliases`.
"""
for module in PUBLIC_MODULES:
for class_ in module.__dict__.values():
if not attrs.has(class_):
continue
if isinstance(class_, _util.NoPublicConstructor):
continue

attributes = [
attr
for attr in attrs.fields(class_)
if attr.init
if attr.alias
not in (
attr.name,
# trio_original_args may not be present in autoattribs
attr.metadata.get("trio_original_args", {}).get("alias"),
)
]

assert attributes == [], class_
Loading