[ty] Add a diagnostic for @functools.total_ordering without a defined comparison method (#22183)
## Summary
This raises a `ValueError` at runtime:
```python
from functools import total_ordering
@total_ordering
class NoOrdering:
def __eq__(self, other: object) -> bool:
return True
```
Specifically:
```
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/functools.py", line 193, in total_ordering
raise ValueError('must define at least one ordering operation: < > <= >=')
ValueError: must define at least one ordering operation: < > <= >=
```
See: https://github.com/astral-sh/ty/issues/1202.
This commit is contained in:
@@ -583,7 +583,7 @@ from module import NotFrozenBase
|
||||
|
||||
@final
|
||||
@dataclass(frozen=True)
|
||||
@total_ordering
|
||||
@total_ordering # error: [invalid-total-ordering]
|
||||
class FrozenChild(NotFrozenBase): # error: [invalid-frozen-dataclass-subclass]
|
||||
y: str
|
||||
```
|
||||
|
||||
@@ -194,12 +194,12 @@ reveal_type(p1 >= p2) # revealed: bool
|
||||
## Missing ordering method
|
||||
|
||||
If a class has `@total_ordering` but doesn't define any ordering method (itself or in a superclass),
|
||||
the decorator would fail at runtime. We don't synthesize methods in this case:
|
||||
a diagnostic is emitted at the decorator site:
|
||||
|
||||
```py
|
||||
from functools import total_ordering
|
||||
|
||||
@total_ordering
|
||||
@total_ordering # error: [invalid-total-ordering]
|
||||
class NoOrdering:
|
||||
def __eq__(self, other: object) -> bool:
|
||||
return True
|
||||
@@ -207,7 +207,7 @@ class NoOrdering:
|
||||
n1 = NoOrdering()
|
||||
n2 = NoOrdering()
|
||||
|
||||
# These should error because no ordering method is defined.
|
||||
# Comparison operators also error because no methods were synthesized.
|
||||
n1 <= n2 # error: [unsupported-operator]
|
||||
n1 >= n2 # error: [unsupported-operator]
|
||||
```
|
||||
|
||||
@@ -61,7 +61,7 @@ mdtest path: crates/ty_python_semantic/resources/mdtest/dataclasses/dataclasses.
|
||||
6 |
|
||||
7 | @final
|
||||
8 | @dataclass(frozen=True)
|
||||
9 | @total_ordering
|
||||
9 | @total_ordering # error: [invalid-total-ordering]
|
||||
10 | class FrozenChild(NotFrozenBase): # error: [invalid-frozen-dataclass-subclass]
|
||||
11 | y: str
|
||||
```
|
||||
@@ -126,6 +126,22 @@ info: rule `invalid-frozen-dataclass-subclass` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-total-ordering]: Class decorated with `@total_ordering` must define at least one ordering method
|
||||
--> src/main.py:9:1
|
||||
|
|
||||
7 | @final
|
||||
8 | @dataclass(frozen=True)
|
||||
9 | @total_ordering # error: [invalid-total-ordering]
|
||||
| ^^^^^^^^^^^^^^^ `FrozenChild` does not define `__lt__`, `__le__`, `__gt__`, or `__ge__`
|
||||
10 | class FrozenChild(NotFrozenBase): # error: [invalid-frozen-dataclass-subclass]
|
||||
11 | y: str
|
||||
|
|
||||
info: The decorator will raise `ValueError` at runtime
|
||||
info: rule `invalid-total-ordering` is enabled by default
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
error[invalid-frozen-dataclass-subclass]: Frozen dataclass cannot inherit from non-frozen dataclass
|
||||
--> src/main.py:8:1
|
||||
@@ -133,7 +149,7 @@ error[invalid-frozen-dataclass-subclass]: Frozen dataclass cannot inherit from n
|
||||
7 | @final
|
||||
8 | @dataclass(frozen=True)
|
||||
| ----------------------- `FrozenChild` dataclass parameters
|
||||
9 | @total_ordering
|
||||
9 | @total_ordering # error: [invalid-total-ordering]
|
||||
10 | class FrozenChild(NotFrozenBase): # error: [invalid-frozen-dataclass-subclass]
|
||||
| ^^^^^^^^^^^^-------------^ Subclass `FrozenChild` is frozen but base class `NotFrozenBase` is not
|
||||
11 | y: str
|
||||
|
||||
Reference in New Issue
Block a user