ovld 0.5.6__py3-none-any.whl → 0.5.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ovld/core.py +6 -0
- ovld/medley.py +2 -1
- ovld/mro.py +41 -5
- ovld/typemap.py +24 -9
- ovld/utils.py +1 -0
- ovld/version.py +1 -1
- {ovld-0.5.6.dist-info → ovld-0.5.8.dist-info}/METADATA +54 -13
- ovld-0.5.8.dist-info/RECORD +18 -0
- ovld-0.5.6.dist-info/RECORD +0 -18
- {ovld-0.5.6.dist-info → ovld-0.5.8.dist-info}/WHEEL +0 -0
- {ovld-0.5.6.dist-info → ovld-0.5.8.dist-info}/licenses/LICENSE +0 -0
ovld/core.py
CHANGED
@@ -45,6 +45,7 @@ def bootstrap_dispatch(ov, name):
|
|
45
45
|
dispatch.register = ov.register
|
46
46
|
dispatch.resolve_for_values = ov.resolve_for_values
|
47
47
|
dispatch.resolve = ov.resolve
|
48
|
+
dispatch.resolve_all = ov.resolve_all
|
48
49
|
dispatch.copy = ov.copy
|
49
50
|
dispatch.variant = ov.variant
|
50
51
|
dispatch.display_methods = ov.display_methods
|
@@ -250,6 +251,11 @@ class Ovld:
|
|
250
251
|
else:
|
251
252
|
return self.map[args]
|
252
253
|
|
254
|
+
def resolve_all(self, *args, **kwargs):
|
255
|
+
"""Yield all methods that match the arguments, in priority order."""
|
256
|
+
self.ensure_compiled()
|
257
|
+
return self.map.resolve_all(*args, **kwargs)
|
258
|
+
|
253
259
|
def register_signature(self, sig, orig_fn):
|
254
260
|
"""Register a function for the given signature."""
|
255
261
|
fn = adapt_function(orig_fn, self, f"{self.__name__}[{sigstring(sig.types)}]")
|
ovld/medley.py
CHANGED
@@ -164,7 +164,7 @@ class medley_cls_dict(dict):
|
|
164
164
|
super().__setitem__(attr, value)
|
165
165
|
|
166
166
|
def __setitem__(self, attr, value):
|
167
|
-
if attr == "__annotations__":
|
167
|
+
if attr == "__annotations__" or attr == "__annotate_func__":
|
168
168
|
self.set_direct(attr, value)
|
169
169
|
return
|
170
170
|
|
@@ -365,6 +365,7 @@ def meld_classes(classes):
|
|
365
365
|
merged = medley_cls_dict(medleys)
|
366
366
|
merged.set_direct("_ovld_codegen_fields", tuple(cg_fields))
|
367
367
|
merged.set_direct("_ovld_medleys", tuple(medleys))
|
368
|
+
merged.set_direct("__annotations__", {name: t for name, t, f in dc_fields})
|
368
369
|
|
369
370
|
if "__qualname__" in merged._combiners:
|
370
371
|
del merged._combiners["__qualname__"]
|
ovld/mro.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from enum import Enum
|
3
3
|
from graphlib import TopologicalSorter
|
4
|
-
from typing import get_args, get_origin
|
4
|
+
from typing import Annotated, Any, get_args, get_origin
|
5
5
|
|
6
6
|
from .utils import UnionTypes, is_dependent
|
7
7
|
|
@@ -52,6 +52,10 @@ def typeorder(t1, t2):
|
|
52
52
|
"""
|
53
53
|
if t1 == t2:
|
54
54
|
return Order.SAME
|
55
|
+
if t1 is Any:
|
56
|
+
return Order.MORE
|
57
|
+
if t2 is Any:
|
58
|
+
return Order.LESS
|
55
59
|
|
56
60
|
if (
|
57
61
|
hasattr(t1, "__type_order__")
|
@@ -67,14 +71,35 @@ def typeorder(t1, t2):
|
|
67
71
|
o1 = get_origin(t1)
|
68
72
|
o2 = get_origin(t2)
|
69
73
|
|
74
|
+
if o1 is Annotated and o2 is Annotated:
|
75
|
+
t1, *a1 = get_args(t1)
|
76
|
+
t2, *a2 = get_args(t2)
|
77
|
+
p1 = max([getattr(ann, "annotation_priority", 0) for ann in a1], default=0)
|
78
|
+
p2 = max([getattr(ann, "annotation_priority", 0) for ann in a2], default=0)
|
79
|
+
if p1 < p2:
|
80
|
+
return Order.MORE
|
81
|
+
elif p2 < p1:
|
82
|
+
return Order.LESS
|
83
|
+
else:
|
84
|
+
return typeorder(t1, t2)
|
85
|
+
|
86
|
+
if o1 is Annotated:
|
87
|
+
if t2 is Annotated:
|
88
|
+
return Order.LESS
|
89
|
+
return typeorder(get_args(t1)[0], t2)
|
90
|
+
if o2 is Annotated:
|
91
|
+
if t1 is Annotated:
|
92
|
+
return Order.MORE
|
93
|
+
return typeorder(t1, get_args(t2)[0])
|
94
|
+
|
70
95
|
if o2 and not o1:
|
71
96
|
return typeorder(t2, t1).opposite()
|
72
97
|
|
73
98
|
if o1:
|
74
99
|
if not o2:
|
75
100
|
order = typeorder(o1, t2)
|
76
|
-
if order is
|
77
|
-
order =
|
101
|
+
if order is Order.SAME:
|
102
|
+
order = Order.LESS
|
78
103
|
return order
|
79
104
|
|
80
105
|
if (order := typeorder(o1, o2)) is not Order.SAME:
|
@@ -93,6 +118,9 @@ def typeorder(t1, t2):
|
|
93
118
|
ords = [typeorder(a1, a2) for a1, a2 in zip(args1, args2)]
|
94
119
|
return Order.merge(ords)
|
95
120
|
|
121
|
+
if not isinstance(t1, type) or not isinstance(t2, type): # pragma: no cover
|
122
|
+
return Order.SAME
|
123
|
+
|
96
124
|
sx = issubclass(t1, t2)
|
97
125
|
sy = issubclass(t2, t1)
|
98
126
|
if sx and sy: # pragma: no cover
|
@@ -108,7 +136,7 @@ def typeorder(t1, t2):
|
|
108
136
|
|
109
137
|
def subclasscheck(t1, t2):
|
110
138
|
"""Check whether t1 is a "subclass" of t2."""
|
111
|
-
if t1 == t2:
|
139
|
+
if t1 == t2 or t2 is Any:
|
112
140
|
return True
|
113
141
|
|
114
142
|
if (
|
@@ -134,12 +162,20 @@ def subclasscheck(t1, t2):
|
|
134
162
|
o1 = get_origin(t1)
|
135
163
|
o2 = get_origin(t2)
|
136
164
|
|
165
|
+
if o1 is Annotated and o2 is Annotated:
|
166
|
+
t1, *a1 = get_args(t1)
|
167
|
+
t2, *a2 = get_args(t2)
|
168
|
+
return subclasscheck(t1, t2) and any(ann in a2 for ann in a1)
|
169
|
+
|
170
|
+
if o1 is Annotated:
|
171
|
+
return t2 is Annotated
|
172
|
+
|
137
173
|
if not isinstance(o1, type):
|
138
174
|
o1 = None
|
139
175
|
if not isinstance(o2, type):
|
140
176
|
o2 = None
|
141
177
|
|
142
|
-
if o1 or o2:
|
178
|
+
if (o1 or o2) and o2 not in UnionTypes:
|
143
179
|
o1 = o1 or t1
|
144
180
|
o2 = o2 or t2
|
145
181
|
if isinstance(o1, type) and isinstance(o2, type) and issubclass(o1, o2):
|
ovld/typemap.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import inspect
|
2
2
|
import math
|
3
3
|
from dataclasses import dataclass
|
4
|
+
from functools import partial
|
4
5
|
from itertools import count
|
5
6
|
from types import CodeType
|
6
7
|
|
@@ -248,7 +249,7 @@ class MultiTypeMap(dict):
|
|
248
249
|
co = h.__code__
|
249
250
|
print(f"{'':{width - 2}} @ {co.co_filename}:{co.co_firstlineno}")
|
250
251
|
|
251
|
-
def
|
252
|
+
def _resolve_all_helper(self, *args, **kwargs):
|
252
253
|
def dependent_match(tup, args):
|
253
254
|
for t, a in zip(tup, args):
|
254
255
|
if isinstance(t, tuple):
|
@@ -258,21 +259,35 @@ class MultiTypeMap(dict):
|
|
258
259
|
return False
|
259
260
|
return True
|
260
261
|
|
261
|
-
message = "No method will be called."
|
262
262
|
argt = [
|
263
263
|
*map(subtler_type, args),
|
264
264
|
*[(k, subtler_type(v)) for k, v in kwargs.items()],
|
265
265
|
]
|
266
|
-
finished = False
|
267
|
-
rank = 1
|
268
266
|
for grp in self.mro(tuple(argt)):
|
269
|
-
|
270
|
-
|
271
|
-
|
267
|
+
yield [
|
268
|
+
(
|
269
|
+
c,
|
270
|
+
dependent_match(
|
271
|
+
self.type_tuples[c.base_handler], [*args, *kwargs.items()]
|
272
|
+
),
|
273
|
+
)
|
272
274
|
for c in grp
|
273
275
|
]
|
274
|
-
|
275
|
-
|
276
|
+
|
277
|
+
def resolve_all(self, *args, **kwargs):
|
278
|
+
for grp in self._resolve_all_helper(*args, **kwargs):
|
279
|
+
for c, m in grp:
|
280
|
+
if m:
|
281
|
+
yield partial(c.handler, *args, **kwargs)
|
282
|
+
|
283
|
+
def display_resolution(self, *args, **kwargs):
|
284
|
+
message = "No method will be called."
|
285
|
+
finished = False
|
286
|
+
rank = 1
|
287
|
+
for grp in self._resolve_all_helper(*args, **kwargs):
|
288
|
+
grp.sort(key=lambda x: x[0].handler.__name__)
|
289
|
+
ambiguous = len([m for _, m in grp if m]) > 1
|
290
|
+
for c, m in grp:
|
276
291
|
handler = c.handler
|
277
292
|
color = "\033[0m"
|
278
293
|
if finished:
|
ovld/utils.py
CHANGED
ovld/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
version = "0.5.
|
1
|
+
version = "0.5.8"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: ovld
|
3
|
-
Version: 0.5.
|
3
|
+
Version: 0.5.8
|
4
4
|
Summary: Overloading Python functions
|
5
5
|
Project-URL: Homepage, https://ovld.readthedocs.io/en/latest/
|
6
6
|
Project-URL: Documentation, https://ovld.readthedocs.io/en/latest/
|
@@ -22,13 +22,53 @@ With ovld, you can write a version of the same function for every type signature
|
|
22
22
|
|
23
23
|
* ⚡️ **[Fast](https://ovld.readthedocs.io/en/latest/compare/#results):** ovld is the fastest multiple dispatch library around, by some margin.
|
24
24
|
* 🚀 [**Variants**](https://ovld.readthedocs.io/en/latest/usage/#variants), [**mixins**](https://ovld.readthedocs.io/en/latest/usage/#mixins) and [**medleys**](https://ovld.readthedocs.io/en/latest/medley) of functions and methods.
|
25
|
-
* 🦄 **[
|
25
|
+
* 🦄 **[Value-based dispatch](https://ovld.readthedocs.io/en/latest/dependent/):** Overloaded functions can depend on more than argument types: they can depend on actual values.
|
26
26
|
* 🔑 **[Extensive](https://ovld.readthedocs.io/en/latest/usage/#keyword-arguments):** Dispatch on functions, methods, positional arguments and even keyword arguments (with some restrictions).
|
27
27
|
* ⚙️ **[Codegen](https://ovld.readthedocs.io/en/latest/codegen/):** (Experimental) For advanced use cases, you can generate custom code for overloads.
|
28
28
|
|
29
|
+
Install with `pip install ovld`
|
30
|
+
|
31
|
+
|
29
32
|
## Example
|
30
33
|
|
31
|
-
|
34
|
+
Define one version of your function for each type signature you want to support. `ovld` supports all basic types, plus literals and value-dependent types such as `Regexp`.
|
35
|
+
|
36
|
+
```python
|
37
|
+
from ovld import ovld
|
38
|
+
from ovld.dependent import Regexp
|
39
|
+
from typing import Literal
|
40
|
+
|
41
|
+
@ovld
|
42
|
+
def f(x: str):
|
43
|
+
return f"The string {x!r}"
|
44
|
+
|
45
|
+
@ovld
|
46
|
+
def f(x: int):
|
47
|
+
return f"The number {x}"
|
48
|
+
|
49
|
+
@ovld
|
50
|
+
def f(x: int, y: int):
|
51
|
+
return "Two numbers!"
|
52
|
+
|
53
|
+
@ovld
|
54
|
+
def f(x: Literal[0]):
|
55
|
+
return "zero"
|
56
|
+
|
57
|
+
@ovld
|
58
|
+
def f(x: Regexp[r"^X"]):
|
59
|
+
return "A string that starts with X"
|
60
|
+
|
61
|
+
assert f("hello") == "The string 'hello'"
|
62
|
+
assert f(3) == "The number 3"
|
63
|
+
assert f(1, 2) == "Two numbers!"
|
64
|
+
assert f(0) == "zero"
|
65
|
+
assert f("XSECRET") == "A string that starts with X"
|
66
|
+
```
|
67
|
+
|
68
|
+
|
69
|
+
## Recursive example
|
70
|
+
|
71
|
+
`ovld` shines particularly with recursive definitions, for example tree maps or serialization. Here we define a function that recursively adds lists of lists and integers:
|
32
72
|
|
33
73
|
```python
|
34
74
|
from ovld import ovld, recurse
|
@@ -38,18 +78,19 @@ def add(x: list, y: list):
|
|
38
78
|
return [recurse(a, b) for a, b in zip(x, y)]
|
39
79
|
|
40
80
|
@ovld
|
41
|
-
def add(x:
|
42
|
-
return
|
81
|
+
def add(x: list, y: int):
|
82
|
+
return [recurse(a, y) for a in x]
|
43
83
|
|
44
84
|
@ovld
|
45
|
-
def add(x:
|
46
|
-
return
|
85
|
+
def add(x: int, y: list):
|
86
|
+
return [recurse(x, a) for a in y]
|
47
87
|
|
48
88
|
@ovld
|
49
|
-
def add(x:
|
89
|
+
def add(x: int, y: int):
|
50
90
|
return x + y
|
51
91
|
|
52
92
|
assert add([1, 2], [3, 4]) == [4, 6]
|
93
|
+
assert add([1, 2, [3]], 7) == [8, 9, [10]]
|
53
94
|
```
|
54
95
|
|
55
96
|
The `recurse` function is special: it will recursively call the current ovld object. You may ask: how is it different from simply calling `add`? The difference is that if you create a *variant* of `add`, `recurse` will automatically call the variant.
|
@@ -63,7 +104,7 @@ A *variant* of an `ovld` is a copy of the `ovld`, with some methods added or cha
|
|
63
104
|
|
64
105
|
```python
|
65
106
|
@add.variant
|
66
|
-
def mul(x:
|
107
|
+
def mul(x: int, y: int):
|
67
108
|
return x * y
|
68
109
|
|
69
110
|
assert mul([1, 2], [3, 4]) == [3, 8]
|
@@ -92,9 +133,9 @@ assert f(10) == 121
|
|
92
133
|
|
93
134
|
Both definitions above have the same type signature, but since the first has higher priority, that is the one that will be called.
|
94
135
|
|
95
|
-
However, that does not mean there is no way to call the second one. Indeed, when the first function calls the special function `call_next(x + 1)`, it will call the next function in
|
136
|
+
However, that does not mean there is no way to call the second one. Indeed, when the first function calls the special function `call_next(x + 1)`, it will call the next function in line, in order of priority and specificity.
|
96
137
|
|
97
|
-
The pattern you see above is how you may wrap each call with some generic behavior. For instance, if you did something like
|
138
|
+
The pattern you see above is how you may wrap each call with some generic behavior. For instance, if you did something like this:
|
98
139
|
|
99
140
|
```python
|
100
141
|
@f.variant(priority=1000)
|
@@ -103,12 +144,12 @@ def f2(x: object)
|
|
103
144
|
return call_next(x)
|
104
145
|
```
|
105
146
|
|
106
|
-
|
147
|
+
The above is effectively a clone of `f` that traces every call. Useful for debugging.
|
107
148
|
|
108
149
|
|
109
150
|
## Dependent types
|
110
151
|
|
111
|
-
A dependent type is a type that depends on a value.
|
152
|
+
A dependent type is a type that depends on a value. This enables dispatching based on the actual value of an argument. The simplest example of a dependent type is `typing.Literal[value]`, which matches one single value. `ovld` also supports `Dependent[bound, check]` for arbitrary checks. For example, this definition of factorial:
|
112
153
|
|
113
154
|
```python
|
114
155
|
from typing import Literal
|
@@ -0,0 +1,18 @@
|
|
1
|
+
ovld/__init__.py,sha256=JuCM8Sj65gobV0KYyLr95cSI23Pi6RYZ7X3_F3fdsSw,1821
|
2
|
+
ovld/abc.py,sha256=4qpZyYwI8dWgY1Oiv5FhdKg2uzNcyWxIpGmGJVcjXrs,1177
|
3
|
+
ovld/codegen.py,sha256=27tmamlanuTPDT-x31ISyqP0wGKW9BCFZJGVyq9qLg8,9728
|
4
|
+
ovld/core.py,sha256=WqZ1lvcAGSri02XZeY73Bj5AKB9RYBCAvHLbyns8u68,17792
|
5
|
+
ovld/dependent.py,sha256=JIgsc_5ddPH51_2IrZ6JW6bWE5RyrrrOwR2e9UvDhZ4,8922
|
6
|
+
ovld/medley.py,sha256=Pq6j4qPl8osyQ4Xp-ktnt2uJXytC9Jo70toS5LYHRf8,12840
|
7
|
+
ovld/mro.py,sha256=5h0JoMGUYNoj4RmVi2uElZB9PGWj_IEvLvBWYtAtYRw,6033
|
8
|
+
ovld/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
ovld/recode.py,sha256=vXg9XLExp_9LdAHO0JWR4wvwHhpOLu2Xcrg9ZYg1nms,16407
|
10
|
+
ovld/signatures.py,sha256=Q8JucSOun0ESGx14aWtHtBzLEiM6FxY5HP3imyqXoDo,8984
|
11
|
+
ovld/typemap.py,sha256=wkLuCc6xa2VZJOMaAhuYYgnNrywhovkQwbkBnoRfCsY,13985
|
12
|
+
ovld/types.py,sha256=CRL6Vuzg5moXgAAhIj2698GvZoyF4HWbUDYz2hKt6us,13373
|
13
|
+
ovld/utils.py,sha256=8nvycMWpTmwGq7ojjDA7yi2NdkU78NBdUlvRk_vDECY,5086
|
14
|
+
ovld/version.py,sha256=IAa3xnF9S8cqANXapf16xG7B-aQG7AHxJ3Iio8QzYuc,18
|
15
|
+
ovld-0.5.8.dist-info/METADATA,sha256=b1vd6kxUQ18CJ7t6b3H8zAmyTZaCpFowtZNq_sxJhSE,10458
|
16
|
+
ovld-0.5.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
17
|
+
ovld-0.5.8.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
|
18
|
+
ovld-0.5.8.dist-info/RECORD,,
|
ovld-0.5.6.dist-info/RECORD
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
ovld/__init__.py,sha256=JuCM8Sj65gobV0KYyLr95cSI23Pi6RYZ7X3_F3fdsSw,1821
|
2
|
-
ovld/abc.py,sha256=4qpZyYwI8dWgY1Oiv5FhdKg2uzNcyWxIpGmGJVcjXrs,1177
|
3
|
-
ovld/codegen.py,sha256=27tmamlanuTPDT-x31ISyqP0wGKW9BCFZJGVyq9qLg8,9728
|
4
|
-
ovld/core.py,sha256=HEREHblKcjM9dhFBr0FNwUCyec7o-9XjCsCfJ23SnNw,17544
|
5
|
-
ovld/dependent.py,sha256=JIgsc_5ddPH51_2IrZ6JW6bWE5RyrrrOwR2e9UvDhZ4,8922
|
6
|
-
ovld/medley.py,sha256=0fseIntzJRCPYXq-tmnxgy5ipNa4ZxR0D_6So0xstdQ,12729
|
7
|
-
ovld/mro.py,sha256=Aw1r5Zz7V9cVBDwWzQ-WNnbpBwoGztgbw3wLAyS6Y60,4863
|
8
|
-
ovld/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
-
ovld/recode.py,sha256=vXg9XLExp_9LdAHO0JWR4wvwHhpOLu2Xcrg9ZYg1nms,16407
|
10
|
-
ovld/signatures.py,sha256=Q8JucSOun0ESGx14aWtHtBzLEiM6FxY5HP3imyqXoDo,8984
|
11
|
-
ovld/typemap.py,sha256=EH3oM_QmX-SHLEz14saVrQtRlqG_ltPyJGORLvcGOFk,13520
|
12
|
-
ovld/types.py,sha256=CRL6Vuzg5moXgAAhIj2698GvZoyF4HWbUDYz2hKt6us,13373
|
13
|
-
ovld/utils.py,sha256=cyy9pcuMhmo1_UdPonH9JT6B9QlI4oH6_JK89cM3_gk,5046
|
14
|
-
ovld/version.py,sha256=FtfC8ptaFH0Unc72bPRynMH_N62bxa4tnazGgIcgnTY,18
|
15
|
-
ovld-0.5.6.dist-info/METADATA,sha256=NuqYyUraNAzGzrl8XUVET_xO6xBSzbPhy5F5AtzITWk,9383
|
16
|
-
ovld-0.5.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
17
|
-
ovld-0.5.6.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
|
18
|
-
ovld-0.5.6.dist-info/RECORD,,
|
File without changes
|
File without changes
|