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 ADDED
@@ -0,0 +1,2 @@
1
+ . .venv/bin/activate
2
+ export PYTHONBREAKPOINT=breakword.breakpoint
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ovld
3
- Version: 0.3.8
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, stateful recursion
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, and you can give an `initial_state` to `@ovld.dispatch` as well.
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, stateful recursion
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, and you can give an `initial_state` to `@ovld.dispatch` as well.
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
- from ovld import ovld
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
- print(smap(A, B))
125
- print(smap_mm(A, B))
126
- print(smap_md(A, B))
127
- print(smap_ii(A, B))
128
- print(smap_b(A, B))
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
@@ -0,0 +1,13 @@
1
+ from ovld import OvldBase, ovld
2
+
3
+
4
+ class Boop(OvldBase):
5
+ @ovld(priority=100)
6
+ def f(self, x: object):
7
+ return self.f.next(x) * 3
8
+
9
+ def f(self, x: int):
10
+ return x * 2
11
+
12
+
13
+ print(Boop().f(7))
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ovld"
3
- version = "0.3.8"
3
+ version = "0.3.9"
4
4
  description = "Overloading Python functions"
5
5
  authors = [
6
6
  { name = "Olivier Breuleux", email = "breuleux@gmail.com" }