ovld 0.4.3__py3-none-any.whl → 0.4.5__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/__init__.py +1 -2
- ovld/abc.py +48 -0
- ovld/core.py +197 -214
- ovld/dependent.py +182 -74
- ovld/mro.py +30 -60
- ovld/recode.py +92 -90
- ovld/typemap.py +9 -31
- ovld/types.py +282 -72
- ovld/utils.py +72 -0
- ovld/version.py +1 -1
- {ovld-0.4.3.dist-info → ovld-0.4.5.dist-info}/METADATA +11 -10
- ovld-0.4.5.dist-info/RECORD +14 -0
- ovld-0.4.3.dist-info/RECORD +0 -13
- {ovld-0.4.3.dist-info → ovld-0.4.5.dist-info}/WHEEL +0 -0
- {ovld-0.4.3.dist-info → ovld-0.4.5.dist-info}/licenses/LICENSE +0 -0
ovld/recode.py
CHANGED
@@ -7,8 +7,7 @@ from functools import reduce
|
|
7
7
|
from itertools import count
|
8
8
|
from types import CodeType, FunctionType
|
9
9
|
|
10
|
-
from .
|
11
|
-
from .utils import Unusable, UsageError
|
10
|
+
from .utils import MISSING, NameDatabase, Unusable, UsageError, subtler_type
|
12
11
|
|
13
12
|
recurse = Unusable(
|
14
13
|
"recurse() can only be used from inside an @ovld-registered function."
|
@@ -18,24 +17,8 @@ call_next = Unusable(
|
|
18
17
|
)
|
19
18
|
|
20
19
|
|
21
|
-
dispatch_template = """
|
22
|
-
from ovld.utils import MISSING
|
23
|
-
|
24
|
-
def __DISPATCH__(self, {args}):
|
25
|
-
{inits}
|
26
|
-
{body}
|
27
|
-
{call}
|
28
|
-
"""
|
29
|
-
|
30
|
-
|
31
|
-
call_template = """
|
32
|
-
method = self.map[({lookup})]
|
33
|
-
return method({posargs})
|
34
|
-
"""
|
35
|
-
|
36
|
-
|
37
20
|
def instantiate_code(symbol, code, inject={}):
|
38
|
-
virtual_file = f"<ovld{hash(code)}>"
|
21
|
+
virtual_file = f"<ovld:{abs(hash(code)):x}>"
|
39
22
|
linecache.cache[virtual_file] = (None, None, splitlines(code), virtual_file)
|
40
23
|
code = compile(source=code, filename=virtual_file, mode="exec")
|
41
24
|
glb = {**inject}
|
@@ -55,7 +38,22 @@ def instantiate_code(symbol, code, inject={}):
|
|
55
38
|
# return rval
|
56
39
|
|
57
40
|
|
58
|
-
|
41
|
+
dispatch_template = """
|
42
|
+
def __WRAP_DISPATCH__(OVLD):
|
43
|
+
def __DISPATCH__({args}):
|
44
|
+
{body}
|
45
|
+
|
46
|
+
return __DISPATCH__
|
47
|
+
"""
|
48
|
+
|
49
|
+
|
50
|
+
call_template = """
|
51
|
+
{mvar} = OVLD.map[({lookup})]
|
52
|
+
return {mvar}({posargs})
|
53
|
+
"""
|
54
|
+
|
55
|
+
|
56
|
+
def generate_dispatch(ov, arganal):
|
59
57
|
def join(li, sep=", ", trail=False):
|
60
58
|
li = [x for x in li if x]
|
61
59
|
rval = sep.join(li)
|
@@ -63,19 +61,35 @@ def generate_dispatch(arganal):
|
|
63
61
|
rval += ","
|
64
62
|
return rval
|
65
63
|
|
66
|
-
|
64
|
+
arganal.compile()
|
65
|
+
|
66
|
+
spr = arganal.strict_positional_required
|
67
|
+
spo = arganal.strict_positional_optional
|
68
|
+
pr = arganal.positional_required
|
69
|
+
po = arganal.positional_optional
|
70
|
+
kr = arganal.keyword_required
|
71
|
+
ko = arganal.keyword_optional
|
67
72
|
|
68
73
|
inits = set()
|
69
74
|
|
70
75
|
kwargsstar = ""
|
71
76
|
targsstar = ""
|
72
77
|
|
73
|
-
args = []
|
78
|
+
args = ["self" if arganal.is_method else ""]
|
74
79
|
body = [""]
|
75
|
-
posargs = ["self
|
80
|
+
posargs = ["self" if arganal.is_method else ""]
|
76
81
|
lookup = []
|
77
82
|
|
78
83
|
i = 0
|
84
|
+
ndb = NameDatabase(default_name="INJECT")
|
85
|
+
|
86
|
+
def lookup_for(x):
|
87
|
+
return ndb[arganal.lookup_for(x)]
|
88
|
+
|
89
|
+
for name in spr + spo + pr + po + kr:
|
90
|
+
ndb.register(name)
|
91
|
+
|
92
|
+
mv = ndb.gensym(desired_name="method")
|
79
93
|
|
80
94
|
for name in spr + spo:
|
81
95
|
if name in spr:
|
@@ -83,10 +97,10 @@ def generate_dispatch(arganal):
|
|
83
97
|
else:
|
84
98
|
args.append(f"{name}=MISSING")
|
85
99
|
posargs.append(name)
|
86
|
-
lookup.append(f"{
|
100
|
+
lookup.append(f"{lookup_for(i)}({name})")
|
87
101
|
i += 1
|
88
102
|
|
89
|
-
if len(po) <= 1:
|
103
|
+
if len(po) <= 1 and (spr or spo):
|
90
104
|
# If there are more than one non-strictly positional optional arguments,
|
91
105
|
# then all positional arguments are strictly positional, because if e.g.
|
92
106
|
# x and y are optional we want x==MISSING to imply that y==MISSING, but
|
@@ -99,7 +113,7 @@ def generate_dispatch(arganal):
|
|
99
113
|
else:
|
100
114
|
args.append(f"{name}=MISSING")
|
101
115
|
posargs.append(name)
|
102
|
-
lookup.append(f"{
|
116
|
+
lookup.append(f"{lookup_for(i)}({name})")
|
103
117
|
i += 1
|
104
118
|
|
105
119
|
if len(po) > 1:
|
@@ -109,14 +123,9 @@ def generate_dispatch(arganal):
|
|
109
123
|
args.append("*")
|
110
124
|
|
111
125
|
for name in kr:
|
112
|
-
lookup_fn = (
|
113
|
-
"self.map.transform"
|
114
|
-
if name in arganal.complex_transforms
|
115
|
-
else "type"
|
116
|
-
)
|
117
126
|
args.append(f"{name}")
|
118
127
|
posargs.append(f"{name}={name}")
|
119
|
-
lookup.append(f"({name!r}, {
|
128
|
+
lookup.append(f"({name!r}, {lookup_for(name)}({name}))")
|
120
129
|
|
121
130
|
for name in ko:
|
122
131
|
args.append(f"{name}=MISSING")
|
@@ -126,9 +135,7 @@ def generate_dispatch(arganal):
|
|
126
135
|
inits.add("TARGS = []")
|
127
136
|
body.append(f"if {name} is not MISSING:")
|
128
137
|
body.append(f" KWARGS[{name!r}] = {name}")
|
129
|
-
body.append(
|
130
|
-
f" TARGS.append(({name!r}, {arganal.lookup_for(name)}({name})))"
|
131
|
-
)
|
138
|
+
body.append(f" TARGS.append(({name!r}, {lookup_for(name)}({name})))")
|
132
139
|
|
133
140
|
posargs.append(kwargsstar)
|
134
141
|
lookup.append(targsstar)
|
@@ -136,6 +143,7 @@ def generate_dispatch(arganal):
|
|
136
143
|
fullcall = call_template.format(
|
137
144
|
lookup=join(lookup, trail=True),
|
138
145
|
posargs=join(posargs),
|
146
|
+
mvar=mv,
|
139
147
|
)
|
140
148
|
|
141
149
|
calls = []
|
@@ -145,35 +153,26 @@ def generate_dispatch(arganal):
|
|
145
153
|
call = call_template.format(
|
146
154
|
lookup=join(lookup[: req + i], trail=True),
|
147
155
|
posargs=join(posargs[: req + i + 1]),
|
156
|
+
mvar=mv,
|
148
157
|
)
|
149
|
-
call = textwrap.indent(call, "
|
158
|
+
call = textwrap.indent(call, " ")
|
150
159
|
calls.append(f"\nif {arg} is MISSING:{call}")
|
151
160
|
calls.append(fullcall)
|
152
161
|
|
162
|
+
lines = [*inits, *body, textwrap.indent("".join(calls), " ")]
|
153
163
|
code = dispatch_template.format(
|
154
|
-
inits=join(inits, sep="\n "),
|
155
164
|
args=join(args),
|
156
|
-
body=join(
|
157
|
-
call=textwrap.indent("".join(calls), " "),
|
165
|
+
body=join(lines, sep="\n ").lstrip(),
|
158
166
|
)
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
def __init__(self, prefix):
|
164
|
-
self.prefix = prefix
|
165
|
-
self.count = count()
|
166
|
-
self.variables = {}
|
167
|
-
|
168
|
-
def add(self, value):
|
169
|
-
if isinstance(value, (int, float, str)):
|
170
|
-
return repr(value)
|
171
|
-
id = f"{self.prefix}{next(self.count)}"
|
172
|
-
self.variables[id] = value
|
173
|
-
return id
|
167
|
+
wr = instantiate_code(
|
168
|
+
"__WRAP_DISPATCH__", code, inject={"MISSING": MISSING, **ndb.variables}
|
169
|
+
)
|
170
|
+
return wr(ov)
|
174
171
|
|
175
172
|
|
176
173
|
def generate_dependent_dispatch(tup, handlers, next_call, slf, name, err, nerr):
|
174
|
+
from .dependent import generate_checking_code, is_dependent
|
175
|
+
|
177
176
|
def to_dict(tup):
|
178
177
|
return dict(
|
179
178
|
entry if isinstance(entry, tuple) else (i, entry)
|
@@ -187,14 +186,14 @@ def generate_dependent_dispatch(tup, handlers, next_call, slf, name, err, nerr):
|
|
187
186
|
return f"ARG{x}" if isinstance(x, int) else f"{x}={x}"
|
188
187
|
|
189
188
|
def codegen(typ, arg):
|
190
|
-
cg = typ
|
189
|
+
cg = generate_checking_code(typ)
|
191
190
|
return cg.template.format(
|
192
|
-
arg=arg, **{k:
|
191
|
+
arg=arg, **{k: ndb[v] for k, v in cg.substitutions.items()}
|
193
192
|
)
|
194
193
|
|
195
194
|
tup = to_dict(tup)
|
196
195
|
handlers = [(h, to_dict(types)) for h, types in handlers]
|
197
|
-
|
196
|
+
ndb = NameDatabase(default_name="INJECT")
|
198
197
|
conjs = []
|
199
198
|
|
200
199
|
exclusive = False
|
@@ -227,7 +226,7 @@ def generate_dependent_dispatch(tup, handlers, next_call, slf, name, err, nerr):
|
|
227
226
|
exclusive = getattr(focus, "exclusive_type", False)
|
228
227
|
|
229
228
|
for i, (h, types) in enumerate(handlers):
|
230
|
-
relevant = [k for k in tup if
|
229
|
+
relevant = [k for k in tup if is_dependent(types[k])]
|
231
230
|
if len(relevant) > 1:
|
232
231
|
# The keyexpr method only works if there is only one condition to check.
|
233
232
|
keyexpr = keyed = None
|
@@ -238,12 +237,15 @@ def generate_dependent_dispatch(tup, handlers, next_call, slf, name, err, nerr):
|
|
238
237
|
conj = "True"
|
239
238
|
conjs.append(conj)
|
240
239
|
|
240
|
+
if len(handlers) == 1:
|
241
|
+
exclusive = True
|
242
|
+
|
241
243
|
argspec = ", ".join(argname(x) for x in tup)
|
242
244
|
argcall = ", ".join(argprovide(x) for x in tup)
|
243
245
|
|
244
246
|
body = []
|
245
247
|
if keyexpr:
|
246
|
-
body.append(f"HANDLER = {
|
248
|
+
body.append(f"HANDLER = {ndb[keyed]}.get({keyexpr}, FALLTHROUGH)")
|
247
249
|
body.append(f"return HANDLER({slf}{argcall})")
|
248
250
|
|
249
251
|
elif exclusive:
|
@@ -263,12 +265,12 @@ def generate_dependent_dispatch(tup, handlers, next_call, slf, name, err, nerr):
|
|
263
265
|
body.append("elif SUMMATION == 0:")
|
264
266
|
body.append(f" return FALLTHROUGH({slf}{argcall})")
|
265
267
|
body.append("else:")
|
266
|
-
body.append(f" raise {
|
268
|
+
body.append(f" raise {ndb[err]}")
|
267
269
|
|
268
270
|
body_text = textwrap.indent("\n".join(body), " ")
|
269
271
|
code = f"def __DEPENDENT_DISPATCH__({slf}{argspec}):\n{body_text}"
|
270
272
|
|
271
|
-
inject =
|
273
|
+
inject = ndb.variables
|
272
274
|
for i, (h, types) in enumerate(handlers):
|
273
275
|
inject[f"HANDLER{i}"] = h
|
274
276
|
|
@@ -366,12 +368,19 @@ def rename_function(fn, newname):
|
|
366
368
|
|
367
369
|
class NameConverter(ast.NodeTransformer):
|
368
370
|
def __init__(
|
369
|
-
self,
|
371
|
+
self,
|
372
|
+
anal,
|
373
|
+
recurse_sym,
|
374
|
+
call_next_sym,
|
375
|
+
ovld_mangled,
|
376
|
+
map_mangled,
|
377
|
+
code_mangled,
|
370
378
|
):
|
371
379
|
self.analysis = anal
|
372
380
|
self.recurse_sym = recurse_sym
|
373
381
|
self.call_next_sym = call_next_sym
|
374
382
|
self.ovld_mangled = ovld_mangled
|
383
|
+
self.map_mangled = map_mangled
|
375
384
|
self.code_mangled = code_mangled
|
376
385
|
self.count = count()
|
377
386
|
|
@@ -400,18 +409,16 @@ class NameConverter(ast.NodeTransformer):
|
|
400
409
|
tmp = f"__TMP{next(self.count)}_"
|
401
410
|
|
402
411
|
def _make_lookup_call(key, arg):
|
412
|
+
name = (
|
413
|
+
"__SUBTLER_TYPE"
|
414
|
+
if self.analysis.lookup_for(key) is subtler_type
|
415
|
+
else "type"
|
416
|
+
)
|
403
417
|
value = ast.NamedExpr(
|
404
418
|
target=ast.Name(id=f"{tmp}{key}", ctx=ast.Store()),
|
405
419
|
value=self.visit(arg),
|
406
420
|
)
|
407
|
-
|
408
|
-
func = ast.Attribute(
|
409
|
-
value=ast.Name(id=f"{tmp}M", ctx=ast.Load()),
|
410
|
-
attr="transform",
|
411
|
-
ctx=ast.Load(),
|
412
|
-
)
|
413
|
-
else:
|
414
|
-
func = ast.Name(id="type", ctx=ast.Load())
|
421
|
+
func = ast.Name(id=name, ctx=ast.Load())
|
415
422
|
return ast.Call(
|
416
423
|
func=func,
|
417
424
|
args=[value],
|
@@ -438,14 +445,7 @@ class NameConverter(ast.NodeTransformer):
|
|
438
445
|
if cn:
|
439
446
|
type_parts.insert(0, ast.Name(id=self.code_mangled, ctx=ast.Load()))
|
440
447
|
method = ast.Subscript(
|
441
|
-
value=ast.
|
442
|
-
target=ast.Name(id=f"{tmp}M", ctx=ast.Store()),
|
443
|
-
value=ast.Attribute(
|
444
|
-
value=ast.Name(id=self.ovld_mangled, ctx=ast.Load()),
|
445
|
-
attr="map",
|
446
|
-
ctx=ast.Load(),
|
447
|
-
),
|
448
|
-
),
|
448
|
+
value=ast.Name(id=self.map_mangled, ctx=ast.Load()),
|
449
449
|
slice=ast.Tuple(
|
450
450
|
elts=type_parts,
|
451
451
|
ctx=ast.Load(),
|
@@ -453,19 +453,14 @@ class NameConverter(ast.NodeTransformer):
|
|
453
453
|
ctx=ast.Load(),
|
454
454
|
)
|
455
455
|
if self.analysis.is_method:
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
attr="__get__",
|
460
|
-
ctx=ast.Load(),
|
461
|
-
),
|
462
|
-
args=[ast.Name(id="self", ctx=ast.Load())],
|
463
|
-
keywords=[],
|
464
|
-
)
|
456
|
+
selfarg = [ast.Name(id="self", ctx=ast.Load())]
|
457
|
+
else:
|
458
|
+
selfarg = []
|
465
459
|
|
466
460
|
new_node = ast.Call(
|
467
461
|
func=method,
|
468
|
-
args=
|
462
|
+
args=selfarg
|
463
|
+
+ [
|
469
464
|
ast.Name(id=f"{tmp}{i}", ctx=ast.Load())
|
470
465
|
for i, arg in enumerate(node.args)
|
471
466
|
],
|
@@ -498,7 +493,10 @@ def adapt_function(fn, ovld, newname):
|
|
498
493
|
"""Create a copy of the function with a different name."""
|
499
494
|
rec_syms = list(
|
500
495
|
_search_names(
|
501
|
-
fn.__code__,
|
496
|
+
fn.__code__,
|
497
|
+
(recurse, ovld, ovld.dispatch),
|
498
|
+
fn.__globals__,
|
499
|
+
fn.__closure__,
|
502
500
|
)
|
503
501
|
)
|
504
502
|
cn_syms = list(
|
@@ -540,6 +538,7 @@ def closure_wrap(tree, fname, names):
|
|
540
538
|
|
541
539
|
def recode(fn, ovld, recurse_sym, call_next_sym, newname):
|
542
540
|
ovld_mangled = f"___OVLD{ovld.id}"
|
541
|
+
map_mangled = f"___MAP{ovld.id}"
|
543
542
|
code_mangled = f"___CODE{next(_current)}"
|
544
543
|
try:
|
545
544
|
src = inspect.getsource(fn)
|
@@ -556,6 +555,7 @@ def recode(fn, ovld, recurse_sym, call_next_sym, newname):
|
|
556
555
|
recurse_sym=recurse_sym,
|
557
556
|
call_next_sym=call_next_sym,
|
558
557
|
ovld_mangled=ovld_mangled,
|
558
|
+
map_mangled=map_mangled,
|
559
559
|
code_mangled=code_mangled,
|
560
560
|
).visit(tree)
|
561
561
|
new.body[0].decorator_list = []
|
@@ -579,6 +579,8 @@ def recode(fn, ovld, recurse_sym, call_next_sym, newname):
|
|
579
579
|
new_fn.__kwdefaults__ = fn.__kwdefaults__
|
580
580
|
new_fn.__annotations__ = fn.__annotations__
|
581
581
|
new_fn = rename_function(new_fn, newname)
|
582
|
-
new_fn.__globals__[
|
582
|
+
new_fn.__globals__["__SUBTLER_TYPE"] = subtler_type
|
583
|
+
new_fn.__globals__[ovld_mangled] = ovld.dispatch
|
584
|
+
new_fn.__globals__[map_mangled] = ovld.map
|
583
585
|
new_fn.__globals__[code_mangled] = new_fn.__code__
|
584
586
|
return new_fn
|
ovld/typemap.py
CHANGED
@@ -1,27 +1,12 @@
|
|
1
1
|
import inspect
|
2
2
|
import math
|
3
|
-
import typing
|
4
3
|
from dataclasses import dataclass
|
5
4
|
from itertools import count
|
6
5
|
from types import CodeType
|
7
6
|
|
8
|
-
from .dependent import DependentType
|
9
7
|
from .mro import sort_types
|
10
8
|
from .recode import generate_dependent_dispatch
|
11
|
-
from .utils import MISSING
|
12
|
-
|
13
|
-
|
14
|
-
class GenericAliasMC(type):
|
15
|
-
def __instancecheck__(cls, obj):
|
16
|
-
return hasattr(obj, "__origin__")
|
17
|
-
|
18
|
-
|
19
|
-
class GenericAlias(metaclass=GenericAliasMC):
|
20
|
-
pass
|
21
|
-
|
22
|
-
|
23
|
-
def is_type_of_type(t):
|
24
|
-
return getattr(t, "__origin__", None) is type
|
9
|
+
from .utils import MISSING, subtler_type
|
25
10
|
|
26
11
|
|
27
12
|
class TypeMap(dict):
|
@@ -123,16 +108,6 @@ class MultiTypeMap(dict):
|
|
123
108
|
self.all = {}
|
124
109
|
self.errors = {}
|
125
110
|
|
126
|
-
def transform(self, obj):
|
127
|
-
if isinstance(obj, GenericAlias):
|
128
|
-
return type[obj]
|
129
|
-
elif obj is typing.Any:
|
130
|
-
return type[object]
|
131
|
-
elif isinstance(obj, type):
|
132
|
-
return type[obj]
|
133
|
-
else:
|
134
|
-
return type(obj)
|
135
|
-
|
136
111
|
def mro(self, obj_t_tup):
|
137
112
|
specificities = {}
|
138
113
|
candidates = None
|
@@ -229,6 +204,8 @@ class MultiTypeMap(dict):
|
|
229
204
|
sig: A Signature object.
|
230
205
|
handler: A function to handle the tuple.
|
231
206
|
"""
|
207
|
+
from .dependent import is_dependent
|
208
|
+
|
232
209
|
self.clear()
|
233
210
|
|
234
211
|
obj_t_tup = sig.types
|
@@ -240,8 +217,7 @@ class MultiTypeMap(dict):
|
|
240
217
|
self.tiebreaks[handler] = sig.tiebreak
|
241
218
|
self.type_tuples[handler] = obj_t_tup
|
242
219
|
self.dependent[handler] = any(
|
243
|
-
|
244
|
-
for t in obj_t_tup
|
220
|
+
is_dependent(t[1] if isinstance(t, tuple) else t) for t in obj_t_tup
|
245
221
|
)
|
246
222
|
|
247
223
|
for i, cls in enumerate(obj_t_tup):
|
@@ -266,19 +242,21 @@ class MultiTypeMap(dict):
|
|
266
242
|
print(f"{'':{width-2}} @ {co.co_filename}:{co.co_firstlineno}")
|
267
243
|
|
268
244
|
def display_resolution(self, *args, **kwargs):
|
245
|
+
from .dependent import is_dependent
|
246
|
+
|
269
247
|
def dependent_match(tup, args):
|
270
248
|
for t, a in zip(tup, args):
|
271
249
|
if isinstance(t, tuple):
|
272
250
|
t = t[1]
|
273
251
|
a = a[1]
|
274
|
-
if
|
252
|
+
if is_dependent(t) and not isinstance(a, t):
|
275
253
|
return False
|
276
254
|
return True
|
277
255
|
|
278
256
|
message = "No method will be called."
|
279
257
|
argt = [
|
280
|
-
*map(
|
281
|
-
*[(k,
|
258
|
+
*map(subtler_type, args),
|
259
|
+
*[(k, subtler_type(v)) for k, v in kwargs.items()],
|
282
260
|
]
|
283
261
|
finished = False
|
284
262
|
rank = 1
|