Skip to content

Commit

Permalink
Fix type errors in Pyright (#999)
Browse files Browse the repository at this point in the history
  • Loading branch information
layday committed Aug 30, 2022
1 parent 7f18d92 commit f7fa5a8
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 5 deletions.
2 changes: 2 additions & 0 deletions changelog.d/999.change.rst
@@ -0,0 +1,2 @@
Made ``attrs.AttrsInstance`` stub available at runtime and fixed type errors
related to the usage of ``attrs.AttrsInstance`` in Pyright.
6 changes: 6 additions & 0 deletions src/attr/__init__.py
Expand Up @@ -52,8 +52,14 @@
ib = attr = attrib
dataclass = partial(attrs, auto_attribs=True) # happy Easter ;)


class AttrsInstance:
pass


__all__ = [
"Attribute",
"AttrsInstance",
"Factory",
"NOTHING",
"asdict",
Expand Down
8 changes: 4 additions & 4 deletions src/attr/__init__.pyi
Expand Up @@ -4,7 +4,6 @@ import sys
from typing import (
Any,
Callable,
ClassVar,
Dict,
Generic,
List,
Expand All @@ -26,6 +25,7 @@ from . import filters as filters
from . import setters as setters
from . import validators as validators
from ._cmp import cmp_using as cmp_using
from ._typing_compat import AttrsInstance_
from ._version_info import VersionInfo

if sys.version_info >= (3, 10):
Expand Down Expand Up @@ -65,9 +65,9 @@ _FieldTransformer = Callable[
# _ValidatorType from working when passed in a list or tuple.
_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]]

# A protocol to be able to statically accept an attrs class.
class AttrsInstance(Protocol):
__attrs_attrs__: ClassVar[Any]
# We subclass this here to keep the protocol's qualified name clean.
class AttrsInstance(AttrsInstance_, Protocol):
pass

# _make --

Expand Down
15 changes: 15 additions & 0 deletions src/attr/_typing_compat.pyi
@@ -0,0 +1,15 @@
from typing import Any, ClassVar, Protocol

# MYPY is a special constant in mypy which works the same way as `TYPE_CHECKING`.
MYPY = False

if MYPY:
# A protocol to be able to statically accept an attrs class.
class AttrsInstance_(Protocol):
__attrs_attrs__: ClassVar[Any]

else:
# For type checkers without plug-in support use an empty protocol that
# will (hopefully) be combined into a union.
class AttrsInstance_(Protocol):
pass
2 changes: 2 additions & 0 deletions src/attrs/__init__.py
Expand Up @@ -3,6 +3,7 @@
from attr import (
NOTHING,
Attribute,
AttrsInstance,
Factory,
__author__,
__copyright__,
Expand Down Expand Up @@ -48,6 +49,7 @@
"assoc",
"astuple",
"Attribute",
"AttrsInstance",
"cmp_using",
"converters",
"define",
Expand Down
28 changes: 28 additions & 0 deletions tests/test_pyright.py
Expand Up @@ -78,3 +78,31 @@ def test_pyright_baseline():
}

assert diagnostics == expected_diagnostics


def test_pyright_attrsinstance_compat(tmp_path):
"""
Test that `AttrsInstance` is compatible with Pyright.
"""
test_pyright_attrsinstance_compat_path = (
tmp_path / "test_pyright_attrsinstance_compat.py"
)
test_pyright_attrsinstance_compat_path.write_text(
"""\
import attrs
# We can assign any old object to `AttrsInstance`.
foo: attrs.AttrsInstance = object()
reveal_type(attrs.AttrsInstance)
"""
)

diagnostics = parse_pyright_output(test_pyright_attrsinstance_compat_path)
expected_diagnostics = {
PyrightDiagnostic(
severity="information",
message='Type of "attrs.AttrsInstance" is "Type[AttrsInstance]"',
),
}
assert diagnostics == expected_diagnostics
2 changes: 1 addition & 1 deletion tox.ini
Expand Up @@ -92,7 +92,7 @@ commands = towncrier build --version UNRELEASED --draft
[testenv:mypy]
deps = mypy>=0.902
commands =
mypy src/attrs/__init__.pyi src/attr/__init__.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
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
mypy tests/typing_example.py


Expand Down

0 comments on commit f7fa5a8

Please sign in to comment.