Skip to content

Commit 8faf44a

Browse files
authored
Fix crash related to functools.total_ordering and forward reference (#12767)
Run the plugin in a later pass to avoid placeholder nodes. Fixes #11728.
1 parent 03901ef commit 8faf44a

File tree

3 files changed

+30
-7
lines changed

3 files changed

+30
-7
lines changed

mypy/plugins/default.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,6 @@ def get_class_decorator_hook(self, fullname: str
9696
) -> Optional[Callable[[ClassDefContext], None]]:
9797
from mypy.plugins import attrs
9898
from mypy.plugins import dataclasses
99-
from mypy.plugins import functools
10099

101100
if fullname in attrs.attr_class_makers:
102101
return attrs.attr_class_maker_callback
@@ -118,17 +117,18 @@ def get_class_decorator_hook(self, fullname: str
118117
)
119118
elif fullname in dataclasses.dataclass_makers:
120119
return dataclasses.dataclass_tag_callback
121-
elif fullname in functools.functools_total_ordering_makers:
122-
return functools.functools_total_ordering_maker_callback
123120

124121
return None
125122

126123
def get_class_decorator_hook_2(self, fullname: str
127124
) -> Optional[Callable[[ClassDefContext], bool]]:
128125
from mypy.plugins import dataclasses
126+
from mypy.plugins import functools
129127

130128
if fullname in dataclasses.dataclass_makers:
131129
return dataclasses.dataclass_class_maker_callback
130+
elif fullname in functools.functools_total_ordering_makers:
131+
return functools.functools_total_ordering_maker_callback
132132

133133
return None
134134

mypy/plugins/functools.py

+6-4
Original file line numberDiff line numberDiff line change
@@ -26,25 +26,25 @@ class _MethodInfo(NamedTuple):
2626

2727

2828
def functools_total_ordering_maker_callback(ctx: mypy.plugin.ClassDefContext,
29-
auto_attribs_default: bool = False) -> None:
29+
auto_attribs_default: bool = False) -> bool:
3030
"""Add dunder methods to classes decorated with functools.total_ordering."""
3131
if ctx.api.options.python_version < (3,):
3232
# This plugin is not supported in Python 2 mode (it's a no-op).
33-
return
33+
return True
3434

3535
comparison_methods = _analyze_class(ctx)
3636
if not comparison_methods:
3737
ctx.api.fail(
3838
'No ordering operation defined when using "functools.total_ordering": < > <= >=',
3939
ctx.reason)
40-
return
40+
return True
4141

4242
# prefer __lt__ to __le__ to __gt__ to __ge__
4343
root = max(comparison_methods, key=lambda k: (comparison_methods[k] is None, k))
4444
root_method = comparison_methods[root]
4545
if not root_method:
4646
# None of the defined comparison methods can be analysed
47-
return
47+
return True
4848

4949
other_type = _find_other_type(root_method)
5050
bool_type = ctx.api.named_type('builtins.bool')
@@ -61,6 +61,8 @@ def functools_total_ordering_maker_callback(ctx: mypy.plugin.ClassDefContext,
6161
args = [Argument(Var('other', other_type), other_type, None, ARG_POS)]
6262
add_method_to_class(ctx.api, ctx.cls, additional_op, args, ret_type)
6363

64+
return True
65+
6466

6567
def _find_other_type(method: _MethodInfo) -> Type:
6668
"""Find the type of the ``other`` argument in a comparison method."""

test-data/unit/check-functools.test

+21
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,24 @@ from typing import TypeVar, Generic
132132
_T = TypeVar('_T')
133133
class cached_property(Generic[_T]): ...
134134
[builtins fixtures/property.pyi]
135+
136+
[case testTotalOrderingWithForwardReference]
137+
from typing import Generic, Any, TypeVar
138+
import functools
139+
140+
T = TypeVar("T", bound="C")
141+
142+
@functools.total_ordering
143+
class D(Generic[T]):
144+
def __lt__(self, other: Any) -> bool:
145+
...
146+
147+
class C:
148+
pass
149+
150+
def f(d: D[C]) -> None:
151+
reveal_type(d.__gt__) # N: Revealed type is "def (other: Any) -> builtins.bool"
152+
153+
d: D[int] # E: Type argument "int" of "D" must be a subtype of "C"
154+
[builtins fixtures/ops.pyi]
155+
[builtins fixtures/dict.pyi]

0 commit comments

Comments
 (0)