Skip to content

Commit 234cc4b

Browse files
authored
Fix method override (#216)
* Fix equality check for Signature * Add test * Remove duplicate tests * Fix faithfulness for new unions * Add notice about mixed element types
1 parent 60d1782 commit 234cc4b

File tree

7 files changed

+79
-353
lines changed

7 files changed

+79
-353
lines changed

docs/types.md

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,41 @@ def f(x: Dict[int, str]) -> str:
4848
return "dict of int to str"
4949
```
5050

51-
**Note:**
5251
Although parametric types such as `List[int]` and `Dict[int, str]` are fully
5352
supported, they do incur a performance penalty.
5453
For optimal performance, is recommended to use parametric types only where necessary.
5554
`Union` and `Optional` do not incur a performance penalty.
5655

56+
````{important}
57+
Plum's type system is powered by [Beartype](https://github.com/beartype/beartype).
58+
To ensure constant-time performance,
59+
Beartype checks the types of containers by checking the type of a random single element.
60+
This means that it is not safe to use containers with mixed element types!
61+
62+
```python
63+
from typing import List
64+
65+
from plum import dispatch
66+
67+
68+
@dispatch
69+
def f(x: List[int]) -> str:
70+
return "list of int"
71+
```
72+
73+
```
74+
>>> f([1, "1"]) # It might happen to check the first element.
75+
"list of int"
76+
77+
>>> f([1, "1"]) # Or it might check the second. :(
78+
NotFoundLookupError: `f([1, '1'])` could not be resolved.
79+
```
80+
81+
In the future, Beartype
82+
[will support exhaustive type checking](https://beartype.readthedocs.io/en/latest/api_decor/#beartype.BeartypeStrategy.On).
83+
Plum already opts into this behaviour and will use it once it becomes available.
84+
````
85+
5786
The type system is *covariant*, as opposed to Julia's type
5887
system, which is *invariant*.
5988
For example, this means that `List[T1]` is a subtype of `List[T2]` whenever

plum/signature.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,16 +121,26 @@ def __rich_console__(self, console, options) -> Segment:
121121

122122
def __eq__(self, other: Any) -> bool:
123123
if isinstance(other, Signature):
124+
if self.varargs is Missing:
125+
self_varargs = Missing
126+
else:
127+
self_varargs = beartype.door.TypeHint(self.varargs)
128+
129+
if other.varargs is Missing:
130+
other_varargs = Missing
131+
else:
132+
other_varargs = beartype.door.TypeHint(other.varargs)
133+
134+
# We don't need to check faithfulness, because that is automatically derived
135+
# from the arguments.
124136
return (
125-
self.types,
126-
self.varargs,
137+
tuple(beartype.door.TypeHint(t) for t in self.types),
138+
self_varargs,
127139
self.precedence,
128-
self.is_faithful,
129140
) == (
130-
other.types,
131-
other.varargs,
141+
tuple(beartype.door.TypeHint(t) for t in other.types),
142+
other_varargs,
132143
other.precedence,
133-
other.is_faithful,
134144
)
135145
return False
136146

plum/type.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ def _is_faithful(x) -> bool:
278278
# counter-example, we will refine this logic.
279279
return True
280280
else:
281-
if origin in {typing.Union, typing.Optional}:
281+
if origin in {typing.Union, UnionType, typing.Optional}:
282282
return all(is_faithful(arg) for arg in args)
283283
else:
284284
return False

tests/advanced/test_advanced.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,17 @@ def test_staticmethod():
360360
A6.f(1.0)
361361
with pytest.raises(NotFoundLookupError):
362362
A6().f(1.0)
363+
364+
365+
def test_equivalent_method_override():
366+
dispatch = Dispatcher()
367+
368+
@dispatch
369+
def f(x: int):
370+
pass
371+
372+
@dispatch
373+
def f(x: Union[int, bool]):
374+
pass
375+
376+
assert len(f.methods) == 1

0 commit comments

Comments
 (0)