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/core.py
CHANGED
@@ -5,36 +5,24 @@ import itertools
|
|
5
5
|
import sys
|
6
6
|
import textwrap
|
7
7
|
import typing
|
8
|
-
from collections import defaultdict
|
8
|
+
from collections import OrderedDict, defaultdict
|
9
9
|
from dataclasses import dataclass, field, replace
|
10
10
|
from functools import cached_property, partial
|
11
|
-
from types import GenericAlias
|
11
|
+
from types import FunctionType, GenericAlias
|
12
12
|
|
13
13
|
from .recode import (
|
14
14
|
Conformer,
|
15
15
|
adapt_function,
|
16
16
|
generate_dispatch,
|
17
|
-
|
17
|
+
rename_code,
|
18
18
|
)
|
19
|
-
from .typemap import MultiTypeMap
|
20
|
-
from .types import normalize_type
|
21
|
-
from .utils import UsageError, keyword_decorator
|
19
|
+
from .typemap import MultiTypeMap
|
20
|
+
from .types import clsstring, normalize_type
|
21
|
+
from .utils import MISSING, UsageError, keyword_decorator, subtler_type
|
22
22
|
|
23
23
|
_current_id = itertools.count()
|
24
24
|
|
25
25
|
|
26
|
-
def _fresh(t):
|
27
|
-
"""Returns a new subclass of type t.
|
28
|
-
|
29
|
-
Each Ovld corresponds to its own class, which allows for specialization of
|
30
|
-
methods.
|
31
|
-
"""
|
32
|
-
methods = {}
|
33
|
-
if not isinstance(getattr(t, "__doc__", None), str):
|
34
|
-
methods["__doc__"] = t.__doc__
|
35
|
-
return type(t.__name__, (t,), methods)
|
36
|
-
|
37
|
-
|
38
26
|
@keyword_decorator
|
39
27
|
def _setattrs(fn, **kwargs):
|
40
28
|
for k, v in kwargs.items():
|
@@ -42,23 +30,107 @@ def _setattrs(fn, **kwargs):
|
|
42
30
|
return fn
|
43
31
|
|
44
32
|
|
45
|
-
|
46
|
-
def
|
47
|
-
|
48
|
-
self.
|
49
|
-
method = getattr(self, fn.__name__)
|
50
|
-
assert method is not first_entry
|
51
|
-
return method(*args, **kwargs)
|
33
|
+
class LazySignature(inspect.Signature):
|
34
|
+
def __init__(self, ovld):
|
35
|
+
super().__init__([])
|
36
|
+
self.ovld = ovld
|
52
37
|
|
53
|
-
|
54
|
-
|
55
|
-
|
38
|
+
def replace(
|
39
|
+
self, *, parameters=inspect._void, return_annotation=inspect._void
|
40
|
+
): # pragma: no cover
|
41
|
+
if parameters is inspect._void:
|
42
|
+
parameters = self.parameters.values()
|
56
43
|
|
44
|
+
if return_annotation is inspect._void:
|
45
|
+
return_annotation = self._return_annotation
|
57
46
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
47
|
+
return inspect.Signature(
|
48
|
+
parameters, return_annotation=return_annotation
|
49
|
+
)
|
50
|
+
|
51
|
+
@property
|
52
|
+
def parameters(self):
|
53
|
+
anal = self.ovld.analyze_arguments()
|
54
|
+
parameters = []
|
55
|
+
if anal.is_method:
|
56
|
+
parameters.append(
|
57
|
+
inspect.Parameter(
|
58
|
+
name="self",
|
59
|
+
kind=inspect._POSITIONAL_ONLY,
|
60
|
+
)
|
61
|
+
)
|
62
|
+
parameters += [
|
63
|
+
inspect.Parameter(
|
64
|
+
name=p,
|
65
|
+
kind=inspect._POSITIONAL_ONLY,
|
66
|
+
)
|
67
|
+
for p in anal.strict_positional_required
|
68
|
+
]
|
69
|
+
parameters += [
|
70
|
+
inspect.Parameter(
|
71
|
+
name=p,
|
72
|
+
kind=inspect._POSITIONAL_ONLY,
|
73
|
+
default=MISSING,
|
74
|
+
)
|
75
|
+
for p in anal.strict_positional_optional
|
76
|
+
]
|
77
|
+
parameters += [
|
78
|
+
inspect.Parameter(
|
79
|
+
name=p,
|
80
|
+
kind=inspect._POSITIONAL_OR_KEYWORD,
|
81
|
+
)
|
82
|
+
for p in anal.positional_required
|
83
|
+
]
|
84
|
+
parameters += [
|
85
|
+
inspect.Parameter(
|
86
|
+
name=p,
|
87
|
+
kind=inspect._POSITIONAL_OR_KEYWORD,
|
88
|
+
default=MISSING,
|
89
|
+
)
|
90
|
+
for p in anal.positional_optional
|
91
|
+
]
|
92
|
+
parameters += [
|
93
|
+
inspect.Parameter(
|
94
|
+
name=p,
|
95
|
+
kind=inspect._KEYWORD_ONLY,
|
96
|
+
)
|
97
|
+
for p in anal.keyword_required
|
98
|
+
]
|
99
|
+
parameters += [
|
100
|
+
inspect.Parameter(
|
101
|
+
name=p,
|
102
|
+
kind=inspect._KEYWORD_ONLY,
|
103
|
+
default=MISSING,
|
104
|
+
)
|
105
|
+
for p in anal.keyword_optional
|
106
|
+
]
|
107
|
+
return OrderedDict({p.name: p for p in parameters})
|
108
|
+
|
109
|
+
|
110
|
+
def bootstrap_dispatch(ov, name):
|
111
|
+
def first_entry(*args, **kwargs):
|
112
|
+
ov.compile()
|
113
|
+
return ov.dispatch(*args, **kwargs)
|
114
|
+
|
115
|
+
dispatch = FunctionType(
|
116
|
+
rename_code(first_entry.__code__, name),
|
117
|
+
{},
|
118
|
+
name,
|
119
|
+
(),
|
120
|
+
first_entry.__closure__,
|
121
|
+
)
|
122
|
+
dispatch.__signature__ = LazySignature(ov)
|
123
|
+
dispatch.__ovld__ = ov
|
124
|
+
dispatch.register = ov.register
|
125
|
+
dispatch.resolve = ov.resolve
|
126
|
+
dispatch.copy = ov.copy
|
127
|
+
dispatch.variant = ov.variant
|
128
|
+
dispatch.display_methods = ov.display_methods
|
129
|
+
dispatch.display_resolution = ov.display_resolution
|
130
|
+
dispatch.add_mixins = ov.add_mixins
|
131
|
+
dispatch.unregister = ov.unregister
|
132
|
+
dispatch.next = ov.next
|
133
|
+
return dispatch
|
62
134
|
|
63
135
|
|
64
136
|
@dataclass(frozen=True)
|
@@ -80,6 +152,7 @@ class Arginfo:
|
|
80
152
|
@dataclass(frozen=True)
|
81
153
|
class Signature:
|
82
154
|
types: tuple
|
155
|
+
return_type: type
|
83
156
|
req_pos: int
|
84
157
|
max_pos: int
|
85
158
|
req_names: frozenset
|
@@ -139,6 +212,7 @@ class Signature:
|
|
139
212
|
|
140
213
|
return cls(
|
141
214
|
types=tuple(typelist),
|
215
|
+
return_type=normalize_type(sig.return_annotation, fn),
|
142
216
|
req_pos=req_pos,
|
143
217
|
max_pos=max_pos,
|
144
218
|
req_names=frozenset(req_names),
|
@@ -149,28 +223,16 @@ class Signature:
|
|
149
223
|
)
|
150
224
|
|
151
225
|
|
152
|
-
def
|
153
|
-
if cls
|
154
|
-
return "*"
|
155
|
-
elif isinstance(cls, tuple):
|
226
|
+
def typemap_entry_string(cls):
|
227
|
+
if isinstance(cls, tuple):
|
156
228
|
key, typ = cls
|
157
229
|
return f"{key}: {clsstring(typ)}"
|
158
|
-
elif is_type_of_type(cls):
|
159
|
-
arg = clsstring(cls.__args__[0])
|
160
|
-
return f"type[{arg}]"
|
161
|
-
elif hasattr(cls, "__origin__"):
|
162
|
-
if cls.__origin__ is typing.Union:
|
163
|
-
return "|".join(map(clsstring, cls.__args__))
|
164
|
-
else:
|
165
|
-
return repr(cls)
|
166
|
-
elif hasattr(cls, "__name__"):
|
167
|
-
return cls.__name__
|
168
230
|
else:
|
169
|
-
return
|
231
|
+
return clsstring(cls)
|
170
232
|
|
171
233
|
|
172
234
|
def sigstring(types):
|
173
|
-
return ", ".join(map(
|
235
|
+
return ", ".join(map(typemap_entry_string, types))
|
174
236
|
|
175
237
|
|
176
238
|
class ArgumentAnalyzer:
|
@@ -181,8 +243,10 @@ class ArgumentAnalyzer:
|
|
181
243
|
self.complex_transforms = set()
|
182
244
|
self.total = 0
|
183
245
|
self.is_method = None
|
246
|
+
self.done = False
|
184
247
|
|
185
248
|
def add(self, fn):
|
249
|
+
self.done = False
|
186
250
|
sig = Signature.extract(fn)
|
187
251
|
self.complex_transforms.update(
|
188
252
|
arg.canonical for arg in sig.arginfo if arg.is_complex
|
@@ -207,6 +271,8 @@ class ArgumentAnalyzer:
|
|
207
271
|
)
|
208
272
|
|
209
273
|
def compile(self):
|
274
|
+
if self.done:
|
275
|
+
return
|
210
276
|
for name, pos in self.name_to_positions.items():
|
211
277
|
if len(pos) != 1:
|
212
278
|
if all(isinstance(p, int) for p in pos):
|
@@ -233,23 +299,23 @@ class ArgumentAnalyzer:
|
|
233
299
|
|
234
300
|
assert strict_positional + positional == p_to_n
|
235
301
|
|
236
|
-
strict_positional_required = [
|
302
|
+
self.strict_positional_required = [
|
237
303
|
f"ARG{pos + 1}"
|
238
304
|
for pos, _ in enumerate(strict_positional)
|
239
305
|
if self.counts[pos][0] == self.total
|
240
306
|
]
|
241
|
-
strict_positional_optional = [
|
307
|
+
self.strict_positional_optional = [
|
242
308
|
f"ARG{pos + 1}"
|
243
309
|
for pos, _ in enumerate(strict_positional)
|
244
310
|
if self.counts[pos][0] != self.total
|
245
311
|
]
|
246
312
|
|
247
|
-
positional_required = [
|
313
|
+
self.positional_required = [
|
248
314
|
names[0]
|
249
315
|
for pos, names in enumerate(positional)
|
250
316
|
if self.counts[pos + len(strict_positional)][0] == self.total
|
251
317
|
]
|
252
|
-
positional_optional = [
|
318
|
+
self.positional_optional = [
|
253
319
|
names[0]
|
254
320
|
for pos, names in enumerate(positional)
|
255
321
|
if self.counts[pos + len(strict_positional)][0] != self.total
|
@@ -260,29 +326,19 @@ class ArgumentAnalyzer:
|
|
260
326
|
for _, (name,) in self.name_to_positions.items()
|
261
327
|
if not isinstance(name, int)
|
262
328
|
]
|
263
|
-
keyword_required = [
|
329
|
+
self.keyword_required = [
|
264
330
|
name for name in keywords if self.counts[name][0] == self.total
|
265
331
|
]
|
266
|
-
keyword_optional = [
|
332
|
+
self.keyword_optional = [
|
267
333
|
name for name in keywords if self.counts[name][0] != self.total
|
268
334
|
]
|
269
|
-
|
270
|
-
return (
|
271
|
-
strict_positional_required,
|
272
|
-
strict_positional_optional,
|
273
|
-
positional_required,
|
274
|
-
positional_optional,
|
275
|
-
keyword_required,
|
276
|
-
keyword_optional,
|
277
|
-
)
|
335
|
+
self.done = True
|
278
336
|
|
279
337
|
def lookup_for(self, key):
|
280
|
-
return
|
281
|
-
"self.map.transform" if key in self.complex_transforms else "type"
|
282
|
-
)
|
338
|
+
return subtler_type if key in self.complex_transforms else type
|
283
339
|
|
284
340
|
|
285
|
-
class
|
341
|
+
class Ovld:
|
286
342
|
"""Overloaded function.
|
287
343
|
|
288
344
|
A function can be added with the `register` method. One of its parameters
|
@@ -305,7 +361,6 @@ class _Ovld:
|
|
305
361
|
self,
|
306
362
|
*,
|
307
363
|
mixins=[],
|
308
|
-
bootstrap=None,
|
309
364
|
name=None,
|
310
365
|
linkback=False,
|
311
366
|
allow_replacement=True,
|
@@ -316,15 +371,14 @@ class _Ovld:
|
|
316
371
|
self.linkback = linkback
|
317
372
|
self.children = []
|
318
373
|
self.allow_replacement = allow_replacement
|
319
|
-
self.bootstrap = bootstrap
|
320
374
|
self.name = name
|
321
375
|
self.shortname = name or f"__OVLD{self.id}"
|
322
376
|
self.__name__ = name
|
323
377
|
self._defns = {}
|
324
378
|
self._locked = False
|
325
379
|
self.mixins = []
|
380
|
+
self.argument_analysis = ArgumentAnalyzer()
|
326
381
|
self.add_mixins(*mixins)
|
327
|
-
self.ocls = _fresh(OvldCall)
|
328
382
|
|
329
383
|
@property
|
330
384
|
def defns(self):
|
@@ -334,11 +388,14 @@ class _Ovld:
|
|
334
388
|
defns.update(self._defns)
|
335
389
|
return defns
|
336
390
|
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
self.
|
391
|
+
def analyze_arguments(self):
|
392
|
+
self.argument_analysis = ArgumentAnalyzer()
|
393
|
+
for key, fn in list(self.defns.items()):
|
394
|
+
self.argument_analysis.add(fn)
|
395
|
+
self.argument_analysis.compile()
|
396
|
+
return self.argument_analysis
|
341
397
|
|
398
|
+
def mkdoc(self):
|
342
399
|
docs = [fn.__doc__ for fn in self.defns.values() if fn.__doc__]
|
343
400
|
if len(docs) == 1:
|
344
401
|
maindoc = docs[0]
|
@@ -359,16 +416,13 @@ class _Ovld:
|
|
359
416
|
return doc
|
360
417
|
|
361
418
|
@property
|
362
|
-
def
|
363
|
-
|
364
|
-
|
419
|
+
def __doc__(self):
|
420
|
+
self.ensure_compiled()
|
421
|
+
return self.mkdoc()
|
365
422
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
[v for k, v in sig.parameters.items() if k != "self"]
|
370
|
-
)
|
371
|
-
return sig
|
423
|
+
@property
|
424
|
+
def __signature__(self):
|
425
|
+
return self.dispatch.__signature__
|
372
426
|
|
373
427
|
def lock(self):
|
374
428
|
self._locked = True
|
@@ -379,11 +433,10 @@ class _Ovld:
|
|
379
433
|
|
380
434
|
def add_mixins(self, *mixins):
|
381
435
|
self._attempt_modify()
|
436
|
+
mixins = [o for m in mixins if (o := to_ovld(m)) is not self]
|
382
437
|
for mixin in mixins:
|
383
438
|
if self.linkback:
|
384
439
|
mixin.children.append(self)
|
385
|
-
if mixin._defns and self.bootstrap is None:
|
386
|
-
self.bootstrap = mixin.bootstrap
|
387
440
|
self.mixins += mixins
|
388
441
|
|
389
442
|
def _key_error(self, key, possibilities=None):
|
@@ -403,28 +456,23 @@ class _Ovld:
|
|
403
456
|
"Note: you can use @ovld(priority=X) to give higher priority to an overload."
|
404
457
|
)
|
405
458
|
|
406
|
-
def rename(self, name):
|
459
|
+
def rename(self, name, shortname=None):
|
407
460
|
"""Rename this Ovld."""
|
408
461
|
self.name = name
|
409
|
-
self.
|
462
|
+
self.shortname = shortname or name
|
463
|
+
self.__name__ = shortname
|
464
|
+
self.dispatch = bootstrap_dispatch(self, name=self.shortname)
|
410
465
|
|
411
466
|
def _set_attrs_from(self, fn):
|
412
467
|
"""Inherit relevant attributes from the function."""
|
413
|
-
if self.bootstrap is None:
|
414
|
-
self.bootstrap = arg0_is_self(fn)
|
415
|
-
|
416
468
|
if self.name is None:
|
417
|
-
self.name = f"{fn.__module__}.{fn.__qualname__}"
|
418
|
-
self.shortname = fn.__name__
|
419
|
-
self.__name__ = fn.__name__
|
420
469
|
self.__qualname__ = fn.__qualname__
|
421
470
|
self.__module__ = fn.__module__
|
471
|
+
self.rename(f"{fn.__module__}.{fn.__qualname__}", fn.__name__)
|
422
472
|
|
423
|
-
def
|
424
|
-
if
|
425
|
-
|
426
|
-
else:
|
427
|
-
return fn
|
473
|
+
def ensure_compiled(self):
|
474
|
+
if not self._compiled:
|
475
|
+
self.compile()
|
428
476
|
|
429
477
|
def compile(self):
|
430
478
|
"""Finalize this overload.
|
@@ -440,40 +488,33 @@ class _Ovld:
|
|
440
488
|
if self not in mixin.children:
|
441
489
|
mixin.lock()
|
442
490
|
|
443
|
-
cls = type(self)
|
444
491
|
if self.name is None:
|
445
492
|
self.name = self.__name__ = f"ovld{self.id}"
|
446
493
|
|
447
494
|
name = self.__name__
|
448
495
|
self.map = MultiTypeMap(name=name, key_error=self._key_error)
|
449
496
|
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
for key, fn in list(self.defns.items()):
|
462
|
-
anal.add(fn)
|
463
|
-
self.argument_analysis = anal
|
464
|
-
dispatch = generate_dispatch(anal)
|
465
|
-
self._dispatch = dispatch
|
466
|
-
target.__call__ = rename_function(dispatch, f"{name}.dispatch")
|
497
|
+
self.analyze_arguments()
|
498
|
+
dispatch = generate_dispatch(self, self.argument_analysis)
|
499
|
+
if not hasattr(self, "dispatch"):
|
500
|
+
self.dispatch = bootstrap_dispatch(self, name=self.shortname)
|
501
|
+
self.dispatch.__code__ = rename_code(dispatch.__code__, self.shortname)
|
502
|
+
self.dispatch.__kwdefaults__ = dispatch.__kwdefaults__
|
503
|
+
self.dispatch.__annotations__ = dispatch.__annotations__
|
504
|
+
self.dispatch.__defaults__ = dispatch.__defaults__
|
505
|
+
self.dispatch.__globals__.update(dispatch.__globals__)
|
506
|
+
self.dispatch.map = self.map
|
507
|
+
self.dispatch.__doc__ = self.mkdoc()
|
467
508
|
|
468
509
|
for key, fn in list(self.defns.items()):
|
469
510
|
self.register_signature(key, fn)
|
470
511
|
|
471
512
|
self._compiled = True
|
472
513
|
|
473
|
-
@_compile_first
|
474
514
|
def resolve(self, *args):
|
475
515
|
"""Find the correct method to call for the given arguments."""
|
476
|
-
|
516
|
+
self.ensure_compiled()
|
517
|
+
return self.map[tuple(map(subtler_type, args))]
|
477
518
|
|
478
519
|
def register_signature(self, sig, orig_fn):
|
479
520
|
"""Register a function for the given signature."""
|
@@ -528,6 +569,8 @@ class _Ovld:
|
|
528
569
|
self.compile()
|
529
570
|
for child in self.children:
|
530
571
|
child._update()
|
572
|
+
if hasattr(self, "dispatch"):
|
573
|
+
self.dispatch.__doc__ = self.mkdoc()
|
531
574
|
|
532
575
|
def copy(self, mixins=[], linkback=False):
|
533
576
|
"""Create a copy of this Ovld.
|
@@ -535,11 +578,7 @@ class _Ovld:
|
|
535
578
|
New functions can be registered to the copy without affecting the
|
536
579
|
original.
|
537
580
|
"""
|
538
|
-
return
|
539
|
-
bootstrap=self.bootstrap,
|
540
|
-
mixins=[self, *mixins],
|
541
|
-
linkback=linkback,
|
542
|
-
)
|
581
|
+
return Ovld(mixins=[self, *mixins], linkback=linkback)
|
543
582
|
|
544
583
|
def variant(self, fn=None, priority=0, **kwargs):
|
545
584
|
"""Decorator to create a variant of this Ovld.
|
@@ -554,104 +593,55 @@ class _Ovld:
|
|
554
593
|
ov.register(fn, priority=priority)
|
555
594
|
return ov
|
556
595
|
|
557
|
-
@_compile_first
|
558
596
|
def __get__(self, obj, cls):
|
559
|
-
if
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
if rval is None:
|
564
|
-
obj.__dict__[key] = rval = self.ocls(self, obj)
|
565
|
-
return rval
|
566
|
-
|
567
|
-
@_compile_first
|
568
|
-
def __getitem__(self, t):
|
569
|
-
if not isinstance(t, tuple):
|
570
|
-
t = (t,)
|
571
|
-
return self.map[t]
|
572
|
-
|
573
|
-
@_compile_first
|
597
|
+
if not self._compiled:
|
598
|
+
self.compile()
|
599
|
+
return self.dispatch.__get__(obj, cls)
|
600
|
+
|
574
601
|
@_setattrs(rename="dispatch")
|
575
|
-
def __call__(self, *args): # pragma: no cover
|
602
|
+
def __call__(self, *args, **kwargs): # pragma: no cover
|
576
603
|
"""Call the overloaded function.
|
577
604
|
|
578
605
|
This should be replaced by an auto-generated function.
|
579
606
|
"""
|
580
|
-
|
581
|
-
|
582
|
-
return
|
607
|
+
if not self._compiled:
|
608
|
+
self.compile()
|
609
|
+
return self.dispatch(*args, **kwargs)
|
583
610
|
|
584
|
-
@_compile_first
|
585
611
|
@_setattrs(rename="next")
|
586
612
|
def next(self, *args):
|
587
613
|
"""Call the next matching method after the caller, in terms of priority or specificity."""
|
588
614
|
fr = sys._getframe(1)
|
589
|
-
key = (fr.f_code, *map(
|
615
|
+
key = (fr.f_code, *map(subtler_type, args))
|
590
616
|
method = self.map[key]
|
591
617
|
return method(*args)
|
592
618
|
|
593
619
|
def __repr__(self):
|
594
620
|
return f"<Ovld {self.name or hex(id(self))}>"
|
595
621
|
|
596
|
-
@_compile_first
|
597
622
|
def display_methods(self):
|
623
|
+
self.ensure_compiled()
|
598
624
|
self.map.display_methods()
|
599
625
|
|
600
|
-
@_compile_first
|
601
626
|
def display_resolution(self, *args, **kwargs):
|
627
|
+
self.ensure_compiled()
|
602
628
|
self.map.display_resolution(*args, **kwargs)
|
603
629
|
|
604
630
|
|
605
631
|
def is_ovld(x):
|
606
632
|
"""Return whether the argument is an ovld function/method."""
|
607
|
-
return isinstance(x,
|
608
|
-
|
609
|
-
|
610
|
-
class OvldCall:
|
611
|
-
"""Context for an Ovld call."""
|
612
|
-
|
613
|
-
def __init__(self, ovld, bind_to):
|
614
|
-
"""Initialize an OvldCall."""
|
615
|
-
self.ovld = ovld
|
616
|
-
self.map = ovld.map
|
617
|
-
self.obj = bind_to
|
618
|
-
|
619
|
-
@property
|
620
|
-
def __name__(self):
|
621
|
-
return self.ovld.__name__
|
622
|
-
|
623
|
-
@property
|
624
|
-
def __doc__(self):
|
625
|
-
return self.ovld.__doc__
|
626
|
-
|
627
|
-
@property
|
628
|
-
def __signature__(self):
|
629
|
-
return self.ovld.__signature__
|
630
|
-
|
631
|
-
def next(self, *args):
|
632
|
-
"""Call the next matching method after the caller, in terms of priority or specificity."""
|
633
|
-
fr = sys._getframe(1)
|
634
|
-
key = (fr.f_code, *map(self.map.transform, args))
|
635
|
-
method = self.map[key]
|
636
|
-
return method(self.obj, *args)
|
637
|
-
|
638
|
-
def resolve(self, *args):
|
639
|
-
"""Find the right method to call for the given arguments."""
|
640
|
-
return self.map[tuple(map(self.map.transform, args))].__get__(self.obj)
|
641
|
-
|
642
|
-
def __call__(self, *args): # pragma: no cover
|
643
|
-
"""Call this overloaded function.
|
644
|
-
|
645
|
-
This should be replaced by an auto-generated function.
|
646
|
-
"""
|
647
|
-
key = tuple(map(self.map.transform, args))
|
648
|
-
method = self.map[key]
|
649
|
-
return method(self.obj, *args)
|
633
|
+
return isinstance(x, Ovld) or isinstance(
|
634
|
+
getattr(x, "__ovld__", False), Ovld
|
635
|
+
)
|
650
636
|
|
651
637
|
|
652
|
-
def
|
653
|
-
"""
|
654
|
-
|
638
|
+
def to_ovld(x):
|
639
|
+
"""Return whether the argument is an ovld function/method."""
|
640
|
+
x = getattr(x, "__ovld__", x)
|
641
|
+
if inspect.isfunction(x):
|
642
|
+
return ovld(x, fresh=True)
|
643
|
+
else:
|
644
|
+
return x if isinstance(x, Ovld) else None
|
655
645
|
|
656
646
|
|
657
647
|
def extend_super(fn):
|
@@ -678,33 +668,24 @@ class ovld_cls_dict(dict):
|
|
678
668
|
def __setitem__(self, attr, value):
|
679
669
|
prev = None
|
680
670
|
if attr in self:
|
681
|
-
prev = self[attr]
|
682
|
-
if inspect.isfunction(prev):
|
683
|
-
prev = ovld(prev, fresh=True)
|
684
|
-
elif not is_ovld(prev): # pragma: no cover
|
685
|
-
prev = None
|
671
|
+
prev = to_ovld(self[attr])
|
686
672
|
elif is_ovld(value) and getattr(value, "_extend_super", False):
|
687
673
|
mixins = []
|
688
674
|
for base in self._bases:
|
689
675
|
if (candidate := getattr(base, attr, None)) is not None:
|
690
|
-
if
|
691
|
-
mixins.append(
|
676
|
+
if mixin := to_ovld(candidate):
|
677
|
+
mixins.append(mixin)
|
692
678
|
if mixins:
|
693
679
|
prev, *others = mixins
|
694
|
-
|
695
|
-
prev = prev.copy()
|
696
|
-
else:
|
697
|
-
prev = ovld(prev, fresh=True)
|
680
|
+
prev = prev.copy()
|
698
681
|
for other in others:
|
699
|
-
|
700
|
-
prev.add_mixins(other)
|
701
|
-
else:
|
702
|
-
prev.register(other)
|
682
|
+
prev.add_mixins(other)
|
703
683
|
else:
|
704
684
|
prev = None
|
705
685
|
|
706
686
|
if prev is not None:
|
707
687
|
if is_ovld(value) and prev is not value:
|
688
|
+
value = getattr(value, "__ovld__", value)
|
708
689
|
if prev.name is None:
|
709
690
|
prev.rename(value.name)
|
710
691
|
prev.add_mixins(value)
|
@@ -713,7 +694,9 @@ class ovld_cls_dict(dict):
|
|
713
694
|
prev.register(value)
|
714
695
|
value = prev
|
715
696
|
|
716
|
-
super().__setitem__(
|
697
|
+
super().__setitem__(
|
698
|
+
attr, value.dispatch if isinstance(value, Ovld) else value
|
699
|
+
)
|
717
700
|
|
718
701
|
|
719
702
|
class OvldMC(type):
|
@@ -774,12 +757,12 @@ def _find_overload(fn, **kwargs):
|
|
774
757
|
dispatch = fr.f_locals.get(fn.__name__, None)
|
775
758
|
|
776
759
|
if dispatch is None:
|
777
|
-
dispatch =
|
760
|
+
dispatch = Ovld(**kwargs)
|
778
761
|
elif not is_ovld(dispatch): # pragma: no cover
|
779
762
|
raise TypeError("@ovld requires Ovld instance")
|
780
763
|
elif kwargs: # pragma: no cover
|
781
764
|
raise TypeError("Cannot configure an overload that already exists")
|
782
|
-
return dispatch
|
765
|
+
return getattr(dispatch, "__ovld__", dispatch)
|
783
766
|
|
784
767
|
|
785
768
|
@keyword_decorator
|
@@ -808,16 +791,16 @@ def ovld(fn, priority=0, fresh=False, **kwargs):
|
|
808
791
|
ovld so that updates can be propagated. (default: False)
|
809
792
|
"""
|
810
793
|
if fresh:
|
811
|
-
dispatch =
|
794
|
+
dispatch = Ovld(**kwargs)
|
812
795
|
else:
|
813
796
|
dispatch = _find_overload(fn, **kwargs)
|
814
|
-
|
797
|
+
dispatch.register(fn, priority=priority)
|
798
|
+
return dispatch.dispatch
|
815
799
|
|
816
800
|
|
817
801
|
__all__ = [
|
818
802
|
"Ovld",
|
819
803
|
"OvldBase",
|
820
|
-
"OvldCall",
|
821
804
|
"OvldMC",
|
822
805
|
"extend_super",
|
823
806
|
"is_ovld",
|