diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6f8aef0..23f5963e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: - run: python -Im tox run -e ${{ env.TOX_PYTHON }}-tests - run: python -Im tox run -e ${{ env.TOX_PYTHON }}-mypy - if: env.IS_PYPY == '0' + if: env.IS_PYPY == '0' && matrix.python-version != '3.7' - name: Upload coverage data uses: actions/upload-artifact@v3 diff --git a/pyproject.toml b/pyproject.toml index 6029e5af..f2531acb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,12 @@ dependencies = ["importlib_metadata;python_version<'3.8'"] dynamic = ["version", "readme"] [project.optional-dependencies] +tests-mypy = [ + 'pytest-mypy-plugins; python_implementation == "CPython" and python_version >= "3.8"', + # Since the mypy error messages keep changing, we have to keep updating this + # pin. + 'mypy>=1.6; python_implementation == "CPython" and python_version >= "3.8"', +] tests-no-zope = [ # For regression test to ensure cloudpickle compat doesn't break. 'cloudpickle; python_implementation == "CPython"', @@ -37,10 +43,7 @@ tests-no-zope = [ # 4.3.0 dropped last use of `convert` "pytest>=4.3.0", "pytest-xdist[psutil]", - # Since the mypy error messages keep changing, we have to keep updating this - # pin. - 'mypy>=1.4; python_implementation == "CPython"', - 'pytest-mypy-plugins; python_implementation == "CPython"', + "attrs[tests-mypy]", ] tests = ["attrs[tests-no-zope]", "zope.interface"] cov = [ diff --git a/tests/test_mypy.yml b/tests/test_mypy.yml index 85293212..70a89670 100644 --- a/tests/test_mypy.yml +++ b/tests/test_mypy.yml @@ -215,10 +215,10 @@ class A: a: int reveal_type(A) # N: Revealed type is "def (a: builtins.int) -> main.A" - reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" - reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" - reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" - reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" + reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`3, other: _AT`3) -> builtins.bool" + reveal_type(A.__le__) # N: Revealed type is "def [_AT] (self: _AT`4, other: _AT`4) -> builtins.bool" + reveal_type(A.__gt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" + reveal_type(A.__ge__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" A(1) < A(2) A(1) <= A(2) @@ -766,7 +766,7 @@ return 'hello' - case: testAttrsUsingBadConverter - mypy_config: strict_optional = False + regex: true main: | import attr from typing import overload @@ -786,14 +786,14 @@ bad_overloaded: int = attr.ib(converter=bad_overloaded_converter) reveal_type(A) out: | - main:15: error: Cannot determine __init__ type from converter [misc] - main:15: error: Argument "converter" has incompatible type "Callable[[], str]"; expected "Callable[[Any], Any]" [arg-type] - main:16: error: Cannot determine __init__ type from converter [misc] - main:16: error: Argument "converter" has incompatible type overloaded function; expected "Callable[[Any], Any]" [arg-type] - main:17: note: Revealed type is "def (bad: Any, bad_overloaded: Any) -> main.A" + main:15: error: Cannot determine __init__ type from converter \[misc\] + main:15: error: Argument "converter" has incompatible type \"Callable\[\[\], str\]\"; expected (\"Callable\[\[Any\], Any\] \| None\"|\"Optional\[Callable\[\[Any\], Any\]\]\") \[arg-type\] + main:16: error: Cannot determine __init__ type from converter \[misc\] + main:16: error: Argument "converter" has incompatible type overloaded function; expected (\"Callable\[\[Any\], Any\] \| None\"|\"Optional\[Callable\[\[Any\], Any\]\]\") \[arg-type\] + main:17: note: Revealed type is "def (bad: Any, bad_overloaded: Any\) -> main.A" - case: testAttrsUsingBadConverterReprocess - mypy_config: strict_optional = False + regex: true main: | import attr from typing import overload @@ -814,11 +814,11 @@ bad_overloaded: int = attr.ib(converter=bad_overloaded_converter) reveal_type(A) out: | - main:16: error: Cannot determine __init__ type from converter [misc] - main:16: error: Argument "converter" has incompatible type "Callable[[], str]"; expected "Callable[[Any], Any]" [arg-type] - main:17: error: Cannot determine __init__ type from converter [misc] - main:17: error: Argument "converter" has incompatible type overloaded function; expected "Callable[[Any], Any]" [arg-type] - main:18: note: Revealed type is "def (bad: Any, bad_overloaded: Any) -> main.A" + main:16: error: Cannot determine __init__ type from converter \[misc\] + main:16: error: Argument \"converter\" has incompatible type \"Callable\[\[\], str\]\"; expected (\"Callable\[\[Any\], Any\] \| None\"|\"Optional\[Callable\[\[Any\], Any\]\]\") \[arg-type\] + main:17: error: Cannot determine __init__ type from converter \[misc\] + main:17: error: Argument "converter" has incompatible type overloaded function; expected (\"Callable\[\[Any\], Any\] \| None\"|\"Optional\[Callable\[\[Any\], Any\]\]\") \[arg-type\] + main:18: note: Revealed type is "def (bad: Any, bad_overloaded: Any\) -> main.A" - case: testAttrsUsingUnsupportedConverter main: | @@ -885,10 +885,10 @@ @attr.s class D(A): pass - reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" - reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" - reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" - reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`-1, other: _AT`-1) -> builtins.bool" + reveal_type(A.__lt__) # N: Revealed type is "def [_AT] (self: _AT`5, other: _AT`5) -> builtins.bool" + reveal_type(B.__lt__) # N: Revealed type is "def [_AT] (self: _AT`6, other: _AT`6) -> builtins.bool" + reveal_type(C.__lt__) # N: Revealed type is "def [_AT] (self: _AT`7, other: _AT`7) -> builtins.bool" + reveal_type(D.__lt__) # N: Revealed type is "def [_AT] (self: _AT`8, other: _AT`8) -> builtins.bool" A() < A() B() < B() @@ -1358,6 +1358,7 @@ reveal_type(B) # N: Revealed type is "def (foo: builtins.int) -> main.B" - case: testFields + regex: true main: | from attrs import define, fields @@ -1366,7 +1367,7 @@ a: int b: str - reveal_type(fields(A)) # N: Revealed type is "Any" + reveal_type(fields(A)) # N: Revealed type is "[Tt]uple\[attr.Attribute\[builtins.int\], attr.Attribute\[builtins.str\], fallback=main.A.__main_A_AttrsAttributes__\]" - case: testFieldsError regex: true @@ -1377,7 +1378,7 @@ a: int b: str - fields(A) # E: Argument 1 to "fields" has incompatible type "[Tt]ype\[A\]"; expected "[Tt]ype\[AttrsInstance\]" \[arg-type\] + fields(A) # E: Argument 1 to "fields" has incompatible type "[Tt]ype\[A\]"; expected an attrs class \[misc\] # E: Argument 1 to "fields" has incompatible type "[Tt]ype\[A\]"; expected "[Tt]ype\[AttrsInstance\]" \[arg-type\] - case: testAsDict main: | diff --git a/tox.ini b/tox.ini index fc3ba4ea..ef24c03a 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,8 @@ min_version = 4 env_list = pre-commit, - py3{7,8,9,10,11,12}-{tests,mypy}, + py3{7,8,9,10,11,12}-tests, + py3{8,9,10,11,12}-mypy, pypy3, pyright, docs, @@ -22,14 +23,12 @@ pass_env = NO_COLOR extras = tests: tests -deps = - mypy: mypy + mypy: tests-mypy commands = tests: pytest {posargs:-n auto} 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:py3{7,10,11}-tests] extras = cov # Python 3.6+ has a number of compile-time warnings on invalid string escapes.