|
5 | 5 |
|
6 | 6 | Features: |
7 | 7 |
|
8 | | -- We return a valid ``typeclass`` generic instance from `@typeclass` contructor |
| 8 | +- We return a valid ``typeclass`` generic instance |
| 9 | + from ``@typeclass`` contructor |
9 | 10 | - We force ``.instance()`` calls to extend the union of allowed types |
10 | 11 | - We ensure that when calling the typeclass'es function |
11 | 12 | we know what values can be used as inputs |
|
15 | 16 |
|
16 | 17 | We use ``pytest-mypy-plugins`` to test that it works correctly, see: |
17 | 18 | https://github.com/TypedDjango/pytest-mypy-plugins |
| 19 | +
|
18 | 20 | """ |
19 | 21 |
|
20 | 22 | from typing import Type |
21 | 23 |
|
22 | 24 | from mypy.plugin import Plugin |
23 | | -from mypy.types import AnyType, UnionType, TypeOfAny, CallableType |
| 25 | +from mypy.types import AnyType, CallableType, TypeOfAny, TypeVarType, UnionType |
24 | 26 |
|
25 | 27 |
|
26 | 28 | def _adjust_arguments(ctx): |
@@ -79,28 +81,28 @@ def _adjust_typeclass_type(self, ctx, instance_type): |
79 | 81 | # It means that function was defined without annotation |
80 | 82 | # or with explicit `Any`, we prevent our Union from polution. |
81 | 83 | # Because `Union[Any, int]` is just `Any`. |
82 | | - lambda type_: not isinstance(type_, AnyType), |
| 84 | + # We also clear accidential type vars. |
| 85 | + self._filter_out_unified_types, |
83 | 86 | [instance_type, ctx.type.args[0]], |
84 | 87 | )) |
85 | 88 |
|
86 | | - if len(unified) == 1: |
87 | | - # We don't want to mix `Union[A]` into the types, |
88 | | - # which sometimes works just like an alias for `Optional[A]`: |
89 | | - ctx.type.args[0] = instance_type |
90 | | - else: |
91 | | - ctx.type.args[0] = UnionType(unified) |
| 89 | + if not isinstance(instance_type, TypeVarType): |
| 90 | + ctx.type.args[0] = UnionType.make_union(unified) |
| 91 | + |
| 92 | + def _filter_out_unified_types(self, type_) -> bool: |
| 93 | + return not isinstance(type_, (AnyType, TypeVarType)) |
92 | 94 |
|
93 | 95 |
|
94 | 96 | class _TypedDecoratorPlugin(Plugin): |
95 | 97 | def get_method_signature_hook(self, fullname: str): |
96 | 98 | """Here we fix the calling method types to accept only valid types.""" |
97 | | - if fullname == 'classes.typeclass._TypeClassMethod.__call__': |
| 99 | + if fullname == 'classes.typeclass.TypeClass.__call__': |
98 | 100 | return _adjust_call_signature |
99 | 101 | return None |
100 | 102 |
|
101 | 103 | def get_method_hook(self, fullname: str): |
102 | 104 | """Here we adjust the typeclass with new allowed types.""" |
103 | | - if fullname == 'classes.typeclass._TypeClassMethod.instance': |
| 105 | + if fullname == 'classes.typeclass.TypeClass.instance': |
104 | 106 | return _AdjustInstanceSignature().instance |
105 | 107 | return None |
106 | 108 |
|
|
0 commit comments