Skip to content

Commit

Permalink
Makes classes compatible with mypy 1.2
Browse files Browse the repository at this point in the history
  • Loading branch information
thepabloaguilar committed Apr 12, 2023
1 parent 1d9d5c9 commit 3c1de4f
Show file tree
Hide file tree
Showing 36 changed files with 1,312 additions and 1,122 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ jobs:
- name: Run tests
run: |
poetry run flake8 .
poetry run mypy classes ./tests/**/*.py
# TODO: Remove `no-warn-unused-ignores` flag once we drop support for Python 3.7 and 3.8
poetry run mypy --no-warn-unused-ignores classes ./tests/**/*.py
poetry run codespell classes tests docs typesafety README.md CONTRIBUTING.md CHANGELOG.md
poetry run pytest classes tests docs/pages README.md
poetry run doc8 -q docs
Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

We follow Semantic Versions since the `0.1.0` release.

## Version 0.5.0 WIP

### Features

- Now requires `mypy>=1.2`

## Version 0.4.1

Expand Down
2 changes: 1 addition & 1 deletion classes/_typeclass.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ def __call__(
],
*args,
**kwargs,
) -> _ReturnType:
) -> _ReturnType: # type: ignore[type-var]
"""
We use this method to actually call a typeclass.
Expand Down
6 changes: 3 additions & 3 deletions classes/contrib/mypy/typeops/call_signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
from mypy.typeops import get_type_vars, make_simplified_union
from mypy.types import CallableType, Instance, ProperType
from mypy.types import Type as MypyType
from mypy.types import TypeVarType, union_items
from mypy.types import TypeVarType
from typing_extensions import Final, final

from classes.contrib.mypy.typeops import type_loader
from classes.contrib.mypy.typeops import type_loader, union

_INCOMPATIBLE_TYPEVAR_MSG: Final = (
'Argument 1 to {0} has incompatible type "{1}"; expected "{2}"'
Expand Down Expand Up @@ -61,7 +61,7 @@ def _infer_type_var(
first_arg: TypeVarType,
passed_type: MypyType,
) -> CallableType:
instance_types = union_items(self._instance_type)
instance_types = union.union_items(self._instance_type)
if isinstance(self._associated_type, Instance):
instance_types.append(_load_supports_type(
first_arg,
Expand Down
8 changes: 4 additions & 4 deletions classes/contrib/mypy/typeops/mro.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
from mypy.subtypes import is_equivalent
from mypy.types import Instance
from mypy.types import Type as MypyType
from mypy.types import UnionType, union_items
from mypy.types import UnionType
from typing_extensions import final

from classes.contrib.mypy.typeops import type_loader
from classes.contrib.mypy.typeops import type_loader, union


@final
Expand Down Expand Up @@ -78,7 +78,7 @@ def __init__(
"""
self._associated_type = associated_type
self._ctx = ctx
self._instance_types = union_items(instance_type)
self._instance_types = union.union_items(instance_type)

# Why do we store added types in a mutable global state?
# Because, these types are hard to replicate without the proper context.
Expand Down Expand Up @@ -181,7 +181,7 @@ def _remove_unified_type(
base = instance_type.type.bases[index]
union_types = [
type_arg
for type_arg in union_items(base.args[0])
for type_arg in union.union_items(base.args[0])
if type_arg not in supports_type.args
]
instance_type.type.bases[index] = supports_type.copy_modified(
Expand Down
2 changes: 1 addition & 1 deletion classes/contrib/mypy/typeops/type_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def load_supports_type(
[arg_type],
)
assert supports_spec
supports_spec.type._promote = None # noqa: WPS437
supports_spec.type._promote = [] # noqa: WPS437
return supports_spec


Expand Down
13 changes: 13 additions & 0 deletions classes/contrib/mypy/typeops/union.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from typing import List

from mypy.types import ProperType
from mypy.types import Type as MypyType
from mypy.types import flatten_nested_unions, get_proper_type


def union_items(typ: MypyType) -> List[ProperType]:
"""Get and flat all union types."""
return [
get_proper_type(union_member)
for union_member in flatten_nested_unions([typ])
]
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

from mypy.erasetype import erase_type
from mypy.plugin import MethodContext
from mypy.sametypes import is_same_type
from mypy.subtypes import is_subtype
from mypy.subtypes import is_same_type, is_subtype
from mypy.types import Instance
from mypy.types import Type as MypyType
from mypy.types import TypedDictType
Expand Down
2,066 changes: 1,113 additions & 953 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,18 @@ python = "^3.7"
typing_extensions = ">=3.10,<5.0"

[tool.poetry.dev-dependencies]
mypy = "^0.942"
mypy = "^1.2.0"

wemake-python-styleguide = "^0.17"
flake8-pytest-style = "^1.6"
nitpick = "^0.32"
nitpick = "^0.33.1"

safety = "^2.3"

pytest = "^7.2"
pytest-cov = "^4.0"
pytest-randomly = "^3.12"
pytest-mypy-plugins = "^1.9"
pytest-mypy-plugins = "^1.10.1"

sphinx = "^5.2"
sphinx-autodoc-typehints = "^1.20"
Expand Down
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ exclude_lines =
plugins =
classes.contrib.mypy.classes_plugin

disable_error_code = empty-body

allow_redefinition = false
check_untyped_defs = true
disallow_any_explicit = true
Expand Down
6 changes: 3 additions & 3 deletions tests/test_associated_type/test_variadic_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,6 @@ def test_subtype_is_variadic():
class Example(AssociatedType[_FirstType]):
"""Correct type."""

assert Example[int]
assert Example[int, int] # type: ignore
assert Example[int, int, str] # type: ignore
assert Example[int] # type: ignore[truthy-function]
assert Example[int, int] # type: ignore[misc]
assert Example[int, int, str] # type: ignore[misc]
2 changes: 1 addition & 1 deletion tests/test_supports.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __instancecheck__(cls, other) -> bool:
)


class _ListOfStr(List[str], metaclass=_ListOfStrMeta):
class _ListOfStr(List[str], metaclass=_ListOfStrMeta): # type: ignore[misc]
"""We use this for testing concrete type calls."""


Expand Down
2 changes: 1 addition & 1 deletion tests/test_typeclass/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def __instancecheck__(cls, other) -> bool:
)


class _ListOfStr(List[str], metaclass=_ListOfStrMeta):
class _ListOfStr(List[str], metaclass=_ListOfStrMeta): # type: ignore[misc]
"""We use this for testing concrete type calls."""


Expand Down
2 changes: 1 addition & 1 deletion tests/test_typeclass/test_typed_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __instancecheck__(cls, arg: object) -> bool:

_Meta = type('_Meta', (_UserDictMeta, type(TypedDict)), {})

class UserDict(_User, metaclass=_Meta):
class UserDict(_User, metaclass=_Meta): # type: ignore[misc]
"""We use this class to represent a typed dict with instance check."""

@typeclass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
def to_json(instance, verbose: bool = False) -> str:
...
out: |
main:6: error: Single direct subclass of "classes._typeclass.AssociatedType" required; got "main.ToJson"
main:6: error: Single direct subclass of "classes._typeclass.AssociatedType" required; got "main.ToJson" [misc]
- case: typeclass_definied_by_multiple_parents
Expand All @@ -28,4 +28,4 @@
def to_json(instance, verbose: bool = False) -> str:
...
out: |
main:9: error: Single direct subclass of "classes._typeclass.AssociatedType" required; got "main.ToJson"
main:9: error: Single direct subclass of "classes._typeclass.AssociatedType" required; got "main.ToJson" [misc]
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def compare(instance) -> int:
...
out: |
main:7: error: Associated types must not have bodies
main:7: error: Associated types must not have bodies [misc]
- case: associated_type_with_attr
Expand All @@ -26,7 +26,7 @@
def compare(instance) -> int:
...
out: |
main:6: error: Associated types must not have bodies
main:6: error: Associated types must not have bodies [misc]
- case: associated_type_with_pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
def compare(instance: List[X]) -> X:
...
out: |
main:10: error: Generic type "main.Compare" with "0" type arguments does not match generic instance declaration "builtins.list[X`-1]" with "1" type arguments
main:10: error: Generic type "main.Compare" with "0" type arguments does not match generic instance declaration "builtins.list[X`-1]" with "1" type arguments [misc]
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
def from_json(instance) -> str:
...
out: |
main:10: error: AssociatedType "main.ToJson" must not be reused, originally associated with "main.from_json"
main:10: error: AssociatedType "main.ToJson" must not be reused, originally associated with "main.from_json" [misc]
4 changes: 2 additions & 2 deletions typesafety/test_associated_type/test_variadic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@
One[int, str]
Two[int]
out: |
main:18: error: Type application has too many types (1 expected)
main:19: error: Type application has too few types (2 expected)
main:18: error: Type application has too many types (1 expected) [misc]
main:19: error: Type application has too few types (2 expected) [misc]
26 changes: 13 additions & 13 deletions typesafety/test_supports_type/test_generic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
x: Supports[Some[int]] = [1, 2, 3]
y = {1, 2, 3}
reveal_type(some(x)) # N: Revealed type is "builtins.int*"
reveal_type(some(y)) # N: Revealed type is "builtins.int*"
reveal_type(some(x)) # N: Revealed type is "builtins.int"
reveal_type(some(y)) # N: Revealed type is "builtins.int"
- case: supports_generic_correct2
Expand All @@ -47,8 +47,8 @@
x: Supports[Some[int, str]] = {1: 'a'}
reveal_type(some(x, 1, 'a')) # N: Revealed type is "builtins.str*"
reveal_type(some({'a': 1}, 'a', 1)) # N: Revealed type is "builtins.int*"
reveal_type(some(x, 1, 'a')) # N: Revealed type is "builtins.str"
reveal_type(some({'a': 1}, 'a', 1)) # N: Revealed type is "builtins.int"
- case: supports_generic_wrong1
Expand All @@ -73,8 +73,8 @@
x: Supports[Some[int]] = {1, 2, 3}
some({1, 2, 3})
out: |
main:17: error: Incompatible types in assignment (expression has type "Set[int]", variable has type "Supports[Some[int]]")
main:18: error: Argument 1 to "some" has incompatible type "Set[int]"; expected "Supports[Some[<nothing>]]"
main:17: error: Incompatible types in assignment (expression has type "Set[int]", variable has type "Supports[Some[int]]") [assignment]
main:18: error: Argument 1 to "some" has incompatible type "Set[int]"; expected "Supports[Some[<nothing>]]" [arg-type]
- case: supports_generic_wrong2
Expand Down Expand Up @@ -105,11 +105,11 @@
y: Supports[Some[int]] = {1, 2, 3}
z: Supports[Some[int]] = 1
out: |
main:17: error: Found different typeclass ".instance" calls, use only "main.some"
main:17: error: Instance "Union[builtins.list[X`-1], builtins.set[X`-1]]" does not match inferred type "builtins.set[_T`1]"
main:22: error: Incompatible types in assignment (expression has type "List[int]", variable has type "Supports[Some[int]]")
main:23: error: Incompatible types in assignment (expression has type "Set[int]", variable has type "Supports[Some[int]]")
main:24: error: Incompatible types in assignment (expression has type "int", variable has type "Supports[Some[int]]")
main:17: error: Found different typeclass ".instance" calls, use only "main.some" [misc]
main:17: error: Instance "Union[builtins.list[X`-1], builtins.set[X`-1]]" does not match inferred type "builtins.set[_T`1]" [misc]
main:22: error: Incompatible types in assignment (expression has type "List[int]", variable has type "Supports[Some[int]]") [assignment]
main:23: error: Incompatible types in assignment (expression has type "Set[int]", variable has type "Supports[Some[int]]") [assignment]
main:24: error: Incompatible types in assignment (expression has type "int", variable has type "Supports[Some[int]]") [assignment]
- case: supports_multiple_one_generic_one_regular
Expand Down Expand Up @@ -177,5 +177,5 @@
...
a: Supports[Some[str], Other[int]]
reveal_type(some(a)) # N: Revealed type is "builtins.str*"
reveal_type(other(a)) # N: Revealed type is "builtins.int*"
reveal_type(some(a)) # N: Revealed type is "builtins.str"
reveal_type(other(a)) # N: Revealed type is "builtins.int"
18 changes: 9 additions & 9 deletions typesafety/test_supports_type/test_regular.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
convert_to_json(1)
convert_to_json('a')
convert_to_json(None) # E: Argument 1 to "convert_to_json" has incompatible type "None"; expected "Supports[ToJson]"
convert_to_json(None) # E: Argument 1 to "convert_to_json" has incompatible type "None"; expected "Supports[ToJson]" [arg-type]
- case: typeclass_supports_type_restriction
Expand Down Expand Up @@ -74,7 +74,7 @@
convert_to_json(to_json, 1)
convert_to_json(to_json, 'a')
convert_to_json(to_json, None) # E: Argument 2 to "convert_to_json" has incompatible type "None"; expected "Supports[ToJson]"
convert_to_json(to_json, None) # E: Argument 2 to "convert_to_json" has incompatible type "None"; expected "Supports[ToJson]" [arg-type]
- case: typeclass_supports_with_function
Expand All @@ -97,7 +97,7 @@
def convert_to_json(instance: Supports[to_json]) -> str:
...
out: |
main:15: error: Function "main.to_json" is not valid as a type
main:15: error: Function "main.to_json" is not valid as a type [valid-type]
main:15: note: Perhaps you need "Callable[...]" or a callback protocol?
Expand All @@ -119,7 +119,7 @@
def convert_to_json(instance: Supports[Other]) -> str:
return to_json(instance)
out: |
main:14: error: Argument 1 to "to_json" has incompatible type "Supports[Other]"; expected "Supports[ToJson]"
main:14: error: Argument 1 to "to_json" has incompatible type "Supports[Other]"; expected "Supports[ToJson]" [arg-type]
- case: supports_annotation
Expand All @@ -139,20 +139,20 @@
return str(instance)
a: Supports[ToJson] = 1
b: Supports[ToJson] = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "Supports[ToJson]")
b: Supports[ToJson] = 'a' # E: Incompatible types in assignment (expression has type "str", variable has type "Supports[ToJson]") [assignment]
- case: supports_type_bound
disable_cache: false
main: |
from classes import Supports, AssociatedType
a: Supports[int] # E: Type argument "builtins.int" of "Supports" must be a subtype of "classes._typeclass.AssociatedType"
a: Supports[int] # E: Type argument "builtins.int" of "Supports" must be a subtype of "classes._typeclass.AssociatedType" [misc]
class A(AssociatedType):
...
b: Supports[A, int] # E: Type argument "builtins.int" of "Supports" must be a subtype of "classes._typeclass.AssociatedType"
b: Supports[A, int] # E: Type argument "builtins.int" of "Supports" must be a subtype of "classes._typeclass.AssociatedType" [misc]
- case: supports_multiple_types
Expand Down Expand Up @@ -261,5 +261,5 @@
a: Supports[ToJson] = 'a'
b: Supports[FromJson] = 'a' # error
out: |
main:21: error: Instance callback is incompatible "def (instance: builtins.str, other: Any) -> builtins.str"; expected "def (instance: builtins.str) -> builtins.str"
main:26: error: Incompatible types in assignment (expression has type "str", variable has type "Supports[FromJson]")
main:21: error: Instance callback is incompatible "def (instance: builtins.str, other: Any) -> builtins.str"; expected "def (instance: builtins.str) -> builtins.str" [misc]
main:26: error: Incompatible types in assignment (expression has type "str", variable has type "Supports[FromJson]") [assignment]
8 changes: 4 additions & 4 deletions typesafety/test_typeclass/test__call__.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
some(B()) # ok
some(C()) # ok
out: |
main:20: error: Argument 1 to "some" has incompatible type "A"; expected "B"
main:20: error: Argument 1 to "some" has incompatible type "A"; expected "B" [arg-type]
- case: typeclass_call_generic_instance_variance
Expand Down Expand Up @@ -98,7 +98,7 @@
some(b) # ok
some(c) # ok
out: |
main:26: error: Argument 1 to "some" has incompatible type "A[int]"; expected "B[<nothing>]"
main:26: error: Argument 1 to "some" has incompatible type "A[int]"; expected "B[<nothing>]" [arg-type]
- case: typeclass_call_variance_union1
Expand Down Expand Up @@ -129,7 +129,7 @@
some(B()) # ok
some(C()) # ok
out: |
main:22: error: Argument 1 to "some" has incompatible type "A"; expected "B"
main:22: error: Argument 1 to "some" has incompatible type "A"; expected "B" [arg-type]
- case: typeclass_call_variance_union2
Expand Down Expand Up @@ -238,4 +238,4 @@
def some(instance) -> int:
...
some() # E: Missing positional argument "instance" in call to "__call__" of "_TypeClass"
some() # E: Missing positional argument "instance" in call to "__call__" of "_TypeClass" [call-arg]
Loading

0 comments on commit 3c1de4f

Please sign in to comment.