ovld 0.3.8__tar.gz → 0.3.9__tar.gz
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-0.3.9/.envrc +2 -0
- {ovld-0.3.8 → ovld-0.3.9}/PKG-INFO +3 -19
- {ovld-0.3.8 → ovld-0.3.9}/README.md +2 -18
- {ovld-0.3.8 → ovld-0.3.9}/bench.py +41 -6
- ovld-0.3.9/doo.py +74 -0
- ovld-0.3.9/edges.py +64 -0
- ovld-0.3.9/explore.py +69 -0
- ovld-0.3.9/nxt.py +13 -0
- {ovld-0.3.8 → ovld-0.3.9}/pyproject.toml +1 -1
- {ovld-0.3.8 → ovld-0.3.9}/src/ovld/core.py +157 -176
- {ovld-0.3.8 → ovld-0.3.9}/src/ovld/mro.py +66 -21
- ovld-0.3.9/src/ovld/recode.py +80 -0
- ovld-0.3.9/src/ovld/version.py +1 -0
- {ovld-0.3.8 → ovld-0.3.9}/tests/test_ovld.py +257 -83
- {ovld-0.3.8 → ovld-0.3.9}/tests/test_typemap.py +47 -16
- ovld-0.3.8/.envrc +0 -1
- ovld-0.3.8/src/ovld/version.py +0 -1
- {ovld-0.3.8 → ovld-0.3.9}/.bsync-snap-20220324145902.852076 +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/.github/workflows/python-package.yml +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/.gitignore +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/.python-version +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/LICENSE +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/requirements-dev.lock +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/requirements.lock +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/src/ovld/__init__.py +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/src/ovld/utils.py +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/tests/__init__.py +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/tests/modules/gingerbread.py +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/tests/test_global.py +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/tests/test_utils.py +0 -0
- {ovld-0.3.8 → ovld-0.3.9}/x.py +0 -0
ovld-0.3.9/.envrc
ADDED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: ovld
|
3
|
-
Version: 0.3.
|
3
|
+
Version: 0.3.9
|
4
4
|
Summary: Overloading Python functions
|
5
5
|
Author-email: Olivier Breuleux <breuleux@gmail.com>
|
6
6
|
License-Expression: MIT
|
@@ -19,7 +19,7 @@ Other features of `ovld`:
|
|
19
19
|
|
20
20
|
* Multiple dispatch for methods (with `metaclass=ovld.OvldMC`)
|
21
21
|
* Create variants of functions
|
22
|
-
* Built-in support for extensible
|
22
|
+
* Built-in support for extensible recursion
|
23
23
|
* Function wrappers
|
24
24
|
* Function postprocessors
|
25
25
|
* Nice stack traces
|
@@ -84,22 +84,6 @@ assert mul([1, 2], [3, 4]) == [3, 8]
|
|
84
84
|
|
85
85
|
A `variant` of a function is a copy which inherits all of the original's implementations but may define new ones. And because `self` is bound to the function that's called at the top level, the implementations for `list`, `tuple` and `dict` will bind `self` to `add` or `mul` depending on which one was called. You may also call `self.super(*args)` to invoke the parent implementation for that type.
|
86
86
|
|
87
|
-
## State
|
88
|
-
|
89
|
-
You can pass `initial_state` to `@ovld` or `variant`. The initial state must be a function that takes no arguments. Its return value will be available in `self.state`. The state is initialized at the top level call, but recursive calls to `self` will preserve it.
|
90
|
-
|
91
|
-
In other words, you can do something like this:
|
92
|
-
|
93
|
-
```python
|
94
|
-
@add.variant(initial_state=lambda: 0)
|
95
|
-
def count(self, x, y):
|
96
|
-
self.state += 1
|
97
|
-
return (f"#{self.state}", x + y)
|
98
|
-
|
99
|
-
assert count([1, 2, 3], [4, 5, 6]) == [("#1", 5), ("#2", 7), ("#3", 9)]
|
100
|
-
```
|
101
|
-
|
102
|
-
The initial_state function can return any object and you can use the state to any purpose (e.g. cache or memoization).
|
103
87
|
|
104
88
|
## Custom dispatch
|
105
89
|
|
@@ -134,7 +118,7 @@ assert add_default([1, 2, "alouette"]) == [2, 4, "alouettealouette"]
|
|
134
118
|
|
135
119
|
There are other uses for this feature, e.g. memoization.
|
136
120
|
|
137
|
-
The normal functions may also have a `self`, which works the same as bootstrapping
|
121
|
+
The normal functions may also have a `self`, which works the same as bootstrapping.
|
138
122
|
|
139
123
|
## Postprocess
|
140
124
|
|
@@ -9,7 +9,7 @@ Other features of `ovld`:
|
|
9
9
|
|
10
10
|
* Multiple dispatch for methods (with `metaclass=ovld.OvldMC`)
|
11
11
|
* Create variants of functions
|
12
|
-
* Built-in support for extensible
|
12
|
+
* Built-in support for extensible recursion
|
13
13
|
* Function wrappers
|
14
14
|
* Function postprocessors
|
15
15
|
* Nice stack traces
|
@@ -74,22 +74,6 @@ assert mul([1, 2], [3, 4]) == [3, 8]
|
|
74
74
|
|
75
75
|
A `variant` of a function is a copy which inherits all of the original's implementations but may define new ones. And because `self` is bound to the function that's called at the top level, the implementations for `list`, `tuple` and `dict` will bind `self` to `add` or `mul` depending on which one was called. You may also call `self.super(*args)` to invoke the parent implementation for that type.
|
76
76
|
|
77
|
-
## State
|
78
|
-
|
79
|
-
You can pass `initial_state` to `@ovld` or `variant`. The initial state must be a function that takes no arguments. Its return value will be available in `self.state`. The state is initialized at the top level call, but recursive calls to `self` will preserve it.
|
80
|
-
|
81
|
-
In other words, you can do something like this:
|
82
|
-
|
83
|
-
```python
|
84
|
-
@add.variant(initial_state=lambda: 0)
|
85
|
-
def count(self, x, y):
|
86
|
-
self.state += 1
|
87
|
-
return (f"#{self.state}", x + y)
|
88
|
-
|
89
|
-
assert count([1, 2, 3], [4, 5, 6]) == [("#1", 5), ("#2", 7), ("#3", 9)]
|
90
|
-
```
|
91
|
-
|
92
|
-
The initial_state function can return any object and you can use the state to any purpose (e.g. cache or memoization).
|
93
77
|
|
94
78
|
## Custom dispatch
|
95
79
|
|
@@ -124,7 +108,7 @@ assert add_default([1, 2, "alouette"]) == [2, 4, "alouettealouette"]
|
|
124
108
|
|
125
109
|
There are other uses for this feature, e.g. memoization.
|
126
110
|
|
127
|
-
The normal functions may also have a `self`, which works the same as bootstrapping
|
111
|
+
The normal functions may also have a `self`, which works the same as bootstrapping.
|
128
112
|
|
129
113
|
## Postprocess
|
130
114
|
|
@@ -2,7 +2,8 @@ import timeit
|
|
2
2
|
|
3
3
|
from multimethod import multimethod
|
4
4
|
from multipledispatch import dispatch
|
5
|
-
|
5
|
+
|
6
|
+
from ovld import ovld, recurse
|
6
7
|
|
7
8
|
# OVLD
|
8
9
|
|
@@ -52,6 +53,30 @@ def smap_b(self, x: object, y: object):
|
|
52
53
|
return x + y
|
53
54
|
|
54
55
|
|
56
|
+
# OVLD C
|
57
|
+
|
58
|
+
|
59
|
+
@ovld
|
60
|
+
def smap_c(x: list, y: list):
|
61
|
+
"""Two."""
|
62
|
+
return [recurse(a, b) for a, b in zip(x, y)]
|
63
|
+
|
64
|
+
|
65
|
+
@ovld
|
66
|
+
def smap_c(x: tuple, y: tuple):
|
67
|
+
return tuple(recurse(a, b) for a, b in zip(x, y))
|
68
|
+
|
69
|
+
|
70
|
+
@ovld
|
71
|
+
def smap_c(x: dict, y: dict):
|
72
|
+
return {k: recurse(v, y[k]) for k, v in x.items()}
|
73
|
+
|
74
|
+
|
75
|
+
@ovld
|
76
|
+
def smap_c(x: object, y: object):
|
77
|
+
return x + y
|
78
|
+
|
79
|
+
|
55
80
|
# multimethods
|
56
81
|
|
57
82
|
|
@@ -121,11 +146,20 @@ def smap_ii(x, y):
|
|
121
146
|
A = {"xs": list(range(50)), "ys": ("o", (6, 7))}
|
122
147
|
B = {"xs": list(range(10, 60)), "ys": ("x", (7, 6))}
|
123
148
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
149
|
+
results = {
|
150
|
+
"smap": smap(A, B),
|
151
|
+
"smap_mm": smap_mm(A, B),
|
152
|
+
"smap_md": smap_md(A, B),
|
153
|
+
"smap_ii": smap_ii(A, B),
|
154
|
+
"smap_b": smap_b(A, B),
|
155
|
+
"smap_c": smap_c(A, B),
|
156
|
+
}
|
157
|
+
|
158
|
+
expected = results["smap"]
|
159
|
+
|
160
|
+
for k, v in results.items():
|
161
|
+
assert v == expected, f"{k} failed"
|
162
|
+
|
129
163
|
|
130
164
|
# breakpoint()
|
131
165
|
|
@@ -134,3 +168,4 @@ print("smap_mm\t", timeit.timeit(lambda: smap_mm(A, B), number=10000))
|
|
134
168
|
print("smap_md\t", timeit.timeit(lambda: smap_md(A, B), number=10000))
|
135
169
|
print("smap_ii\t", timeit.timeit(lambda: smap_ii(A, B), number=10000))
|
136
170
|
print("smap_b\t", timeit.timeit(lambda: smap_b(A, B), number=10000))
|
171
|
+
print("smap_c\t", timeit.timeit(lambda: smap_c(A, B), number=10000))
|
ovld-0.3.9/doo.py
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from types import UnionType
|
3
|
+
|
4
|
+
from ovld import ovld
|
5
|
+
from ovld.core import OvldBase
|
6
|
+
|
7
|
+
|
8
|
+
class Poof[T]:
|
9
|
+
pass
|
10
|
+
|
11
|
+
|
12
|
+
class Flax(OvldBase):
|
13
|
+
@ovld
|
14
|
+
def f(self, x: object, y: type[Poof[object]]):
|
15
|
+
return 1234
|
16
|
+
|
17
|
+
def f(self, frm: int, to: type[int]):
|
18
|
+
return to(frm)
|
19
|
+
|
20
|
+
def f(self, frm: float, to: type[float]):
|
21
|
+
return to(frm)
|
22
|
+
|
23
|
+
def f(self, frm: str, to: type[str]):
|
24
|
+
return to(frm)
|
25
|
+
|
26
|
+
def f(self, frm: str, to: type[Enum]):
|
27
|
+
return to(frm)
|
28
|
+
|
29
|
+
def f(self, frm: object, to: UnionType):
|
30
|
+
for t in to.__args__:
|
31
|
+
try:
|
32
|
+
return self.deserialize(frm, t)
|
33
|
+
except TypeError:
|
34
|
+
continue
|
35
|
+
else:
|
36
|
+
return self.deserialize.next(frm, to)
|
37
|
+
|
38
|
+
def f(self, frm: dict, to: type[Poof[object]]):
|
39
|
+
model = self.model(to)
|
40
|
+
if model is NotImplemented:
|
41
|
+
return self.deserialize.next(frm, to)
|
42
|
+
else:
|
43
|
+
des = {
|
44
|
+
k: self.deserialize(v, model.fields_by_name[k].type)
|
45
|
+
for k, v in frm.items()
|
46
|
+
}
|
47
|
+
return model.builder(**des)
|
48
|
+
|
49
|
+
def f(self, frm: dict, to: type[object]):
|
50
|
+
model = self.model(to)
|
51
|
+
if model is NotImplemented:
|
52
|
+
return self.deserialize.next(frm, to)
|
53
|
+
else:
|
54
|
+
return self.deserialize(frm, model)
|
55
|
+
|
56
|
+
# def f(self, frm: dict, typ: Model):
|
57
|
+
# des = {k: self.deserialize(v, typ.fields_by_name[k].type) for k, v in frm.items()}
|
58
|
+
# return typ.builder(**des)
|
59
|
+
|
60
|
+
|
61
|
+
@ovld
|
62
|
+
def f(x: object, y: type[Poof[object]]):
|
63
|
+
return 12345
|
64
|
+
|
65
|
+
|
66
|
+
print(f(3, Poof[int]))
|
67
|
+
print(Flax().f(3, Poof[int]))
|
68
|
+
|
69
|
+
|
70
|
+
# print("=" * 80)
|
71
|
+
# print(compose_mro(list[int], {list[object]}, set()))
|
72
|
+
|
73
|
+
# print("=" * 80)
|
74
|
+
# print(compose_mro(Poof[int], {Poof[object]}, set()))
|
ovld-0.3.9/edges.py
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
from typing import Protocol, runtime_checkable
|
3
|
+
|
4
|
+
from ovld import Dataclass, ovld
|
5
|
+
|
6
|
+
|
7
|
+
@runtime_checkable
|
8
|
+
class SupportsRead(Protocol):
|
9
|
+
def read(self, amount: int) -> bytes: ...
|
10
|
+
|
11
|
+
|
12
|
+
@runtime_checkable
|
13
|
+
class SupportsWrite(Protocol):
|
14
|
+
def write(self, amount: int) -> bytes: ...
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass
|
18
|
+
class Reader:
|
19
|
+
def read(self):
|
20
|
+
return 1
|
21
|
+
|
22
|
+
def write(self):
|
23
|
+
return 1
|
24
|
+
|
25
|
+
|
26
|
+
@dataclass
|
27
|
+
class Writer:
|
28
|
+
def write(self):
|
29
|
+
return 1
|
30
|
+
|
31
|
+
|
32
|
+
@ovld
|
33
|
+
def f(x: SupportsRead):
|
34
|
+
return "yes"
|
35
|
+
|
36
|
+
|
37
|
+
@ovld
|
38
|
+
def f(x: SupportsWrite):
|
39
|
+
return "yes"
|
40
|
+
|
41
|
+
|
42
|
+
# @ovld
|
43
|
+
# def f(x: Reader):
|
44
|
+
# return "R" + f.next(x)
|
45
|
+
|
46
|
+
|
47
|
+
@ovld # (priority=-1)
|
48
|
+
def f(x: Dataclass):
|
49
|
+
return "DC"
|
50
|
+
|
51
|
+
|
52
|
+
@ovld
|
53
|
+
def f(x: int):
|
54
|
+
return "w/e"
|
55
|
+
|
56
|
+
|
57
|
+
@ovld
|
58
|
+
def f(x):
|
59
|
+
return "no"
|
60
|
+
|
61
|
+
|
62
|
+
# print(f(1))
|
63
|
+
print(f(Reader()))
|
64
|
+
# print(f(Writer()))
|
ovld-0.3.9/explore.py
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# import dis
|
2
|
+
|
3
|
+
# z = 10
|
4
|
+
|
5
|
+
|
6
|
+
# def foo(x):
|
7
|
+
# def faf(y):
|
8
|
+
# return x + y + z
|
9
|
+
|
10
|
+
# return faf
|
11
|
+
|
12
|
+
|
13
|
+
# fn = foo(4)
|
14
|
+
|
15
|
+
# bytecode = dis.Bytecode(fn)
|
16
|
+
# print("=" * 80)
|
17
|
+
# for instr in bytecode:
|
18
|
+
# print(instr)
|
19
|
+
|
20
|
+
# breakpoint()
|
21
|
+
|
22
|
+
|
23
|
+
from wrapt import ObjectProxy
|
24
|
+
|
25
|
+
from ovld import ovld, recurse
|
26
|
+
|
27
|
+
|
28
|
+
class AnnotatedProxy(ObjectProxy):
|
29
|
+
def __init__(self, wrapped, **ann):
|
30
|
+
super().__init__(wrapped)
|
31
|
+
self._self_ann = ann
|
32
|
+
|
33
|
+
@property
|
34
|
+
def _(self):
|
35
|
+
return self._self_ann
|
36
|
+
|
37
|
+
|
38
|
+
# @ovld(priority=10)
|
39
|
+
# def f(x: object):
|
40
|
+
# return recurse.next(x)
|
41
|
+
|
42
|
+
|
43
|
+
@ovld
|
44
|
+
def f(xs: list):
|
45
|
+
return [recurse(x) for x in xs]
|
46
|
+
|
47
|
+
|
48
|
+
@ovld
|
49
|
+
def f(xs: dict):
|
50
|
+
return {recurse(k): recurse(v) for k, v in xs.items()}
|
51
|
+
|
52
|
+
|
53
|
+
@ovld
|
54
|
+
def f(x: str):
|
55
|
+
return x
|
56
|
+
|
57
|
+
|
58
|
+
@ovld
|
59
|
+
def f(x: int):
|
60
|
+
return x / 0
|
61
|
+
|
62
|
+
|
63
|
+
# # f({"a": [[[[1, 2, 3]]]]})
|
64
|
+
# # breakpoint()
|
65
|
+
# res = f({"a": [[[[1, 2, 3]]]]})
|
66
|
+
# print(res)
|
67
|
+
|
68
|
+
# data = {"a": 2}
|
69
|
+
# print(f(AnnotatedProxy(data, x=4)))
|
ovld-0.3.9/nxt.py
ADDED