ovld 0.4.4__py3-none-any.whl → 0.4.6__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 CHANGED
@@ -4,7 +4,6 @@ from . import abc # noqa: F401
4
4
  from .core import (
5
5
  Ovld,
6
6
  OvldBase,
7
- OvldCall,
8
7
  OvldMC,
9
8
  extend_super,
10
9
  is_ovld,
@@ -53,7 +52,6 @@ __all__ = [
53
52
  "MultiTypeMap",
54
53
  "Ovld",
55
54
  "OvldBase",
56
- "OvldCall",
57
55
  "OvldMC",
58
56
  "TypeMap",
59
57
  "extend_super",
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
- rename_function,
17
+ rename_code,
18
18
  )
19
19
  from .typemap import MultiTypeMap
20
20
  from .types import clsstring, normalize_type
21
- from .utils import UsageError, keyword_decorator, subtler_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
- @keyword_decorator
46
- def _compile_first(fn, rename=None):
47
- def first_entry(self, *args, **kwargs):
48
- self.compile()
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
- first_entry._replace_by = fn
54
- first_entry._rename = rename
55
- return first_entry
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
- def arg0_is_self(fn):
59
- sgn = inspect.signature(fn)
60
- params = list(sgn.parameters.values())
61
- return params and params[0].name == "self"
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)
@@ -171,8 +243,10 @@ class ArgumentAnalyzer:
171
243
  self.complex_transforms = set()
172
244
  self.total = 0
173
245
  self.is_method = None
246
+ self.done = False
174
247
 
175
248
  def add(self, fn):
249
+ self.done = False
176
250
  sig = Signature.extract(fn)
177
251
  self.complex_transforms.update(
178
252
  arg.canonical for arg in sig.arginfo if arg.is_complex
@@ -197,6 +271,8 @@ class ArgumentAnalyzer:
197
271
  )
198
272
 
199
273
  def compile(self):
274
+ if self.done:
275
+ return
200
276
  for name, pos in self.name_to_positions.items():
201
277
  if len(pos) != 1:
202
278
  if all(isinstance(p, int) for p in pos):
@@ -223,23 +299,23 @@ class ArgumentAnalyzer:
223
299
 
224
300
  assert strict_positional + positional == p_to_n
225
301
 
226
- strict_positional_required = [
302
+ self.strict_positional_required = [
227
303
  f"ARG{pos + 1}"
228
304
  for pos, _ in enumerate(strict_positional)
229
305
  if self.counts[pos][0] == self.total
230
306
  ]
231
- strict_positional_optional = [
307
+ self.strict_positional_optional = [
232
308
  f"ARG{pos + 1}"
233
309
  for pos, _ in enumerate(strict_positional)
234
310
  if self.counts[pos][0] != self.total
235
311
  ]
236
312
 
237
- positional_required = [
313
+ self.positional_required = [
238
314
  names[0]
239
315
  for pos, names in enumerate(positional)
240
316
  if self.counts[pos + len(strict_positional)][0] == self.total
241
317
  ]
242
- positional_optional = [
318
+ self.positional_optional = [
243
319
  names[0]
244
320
  for pos, names in enumerate(positional)
245
321
  if self.counts[pos + len(strict_positional)][0] != self.total
@@ -250,27 +326,19 @@ class ArgumentAnalyzer:
250
326
  for _, (name,) in self.name_to_positions.items()
251
327
  if not isinstance(name, int)
252
328
  ]
253
- keyword_required = [
329
+ self.keyword_required = [
254
330
  name for name in keywords if self.counts[name][0] == self.total
255
331
  ]
256
- keyword_optional = [
332
+ self.keyword_optional = [
257
333
  name for name in keywords if self.counts[name][0] != self.total
258
334
  ]
259
-
260
- return (
261
- strict_positional_required,
262
- strict_positional_optional,
263
- positional_required,
264
- positional_optional,
265
- keyword_required,
266
- keyword_optional,
267
- )
335
+ self.done = True
268
336
 
269
337
  def lookup_for(self, key):
270
338
  return subtler_type if key in self.complex_transforms else type
271
339
 
272
340
 
273
- class _Ovld:
341
+ class Ovld:
274
342
  """Overloaded function.
275
343
 
276
344
  A function can be added with the `register` method. One of its parameters
@@ -293,7 +361,6 @@ class _Ovld:
293
361
  self,
294
362
  *,
295
363
  mixins=[],
296
- bootstrap=None,
297
364
  name=None,
298
365
  linkback=False,
299
366
  allow_replacement=True,
@@ -304,15 +371,14 @@ class _Ovld:
304
371
  self.linkback = linkback
305
372
  self.children = []
306
373
  self.allow_replacement = allow_replacement
307
- self.bootstrap = bootstrap
308
374
  self.name = name
309
375
  self.shortname = name or f"__OVLD{self.id}"
310
376
  self.__name__ = name
311
377
  self._defns = {}
312
378
  self._locked = False
313
379
  self.mixins = []
380
+ self.argument_analysis = ArgumentAnalyzer()
314
381
  self.add_mixins(*mixins)
315
- self.ocls = _fresh(OvldCall)
316
382
 
317
383
  @property
318
384
  def defns(self):
@@ -322,11 +388,14 @@ class _Ovld:
322
388
  defns.update(self._defns)
323
389
  return defns
324
390
 
325
- @property
326
- def __doc__(self):
327
- if not self._compiled:
328
- self.compile()
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
329
397
 
398
+ def mkdoc(self):
330
399
  docs = [fn.__doc__ for fn in self.defns.values() if fn.__doc__]
331
400
  if len(docs) == 1:
332
401
  maindoc = docs[0]
@@ -347,16 +416,13 @@ class _Ovld:
347
416
  return doc
348
417
 
349
418
  @property
350
- def __signature__(self):
351
- if not self._compiled:
352
- self.compile()
419
+ def __doc__(self):
420
+ self.ensure_compiled()
421
+ return self.mkdoc()
353
422
 
354
- sig = inspect.signature(self._dispatch)
355
- if not self.argument_analysis.is_method:
356
- sig = inspect.Signature(
357
- [v for k, v in sig.parameters.items() if k != "self"]
358
- )
359
- return sig
423
+ @property
424
+ def __signature__(self):
425
+ return self.dispatch.__signature__
360
426
 
361
427
  def lock(self):
362
428
  self._locked = True
@@ -367,11 +433,10 @@ class _Ovld:
367
433
 
368
434
  def add_mixins(self, *mixins):
369
435
  self._attempt_modify()
436
+ mixins = [o for m in mixins if (o := to_ovld(m)) is not self]
370
437
  for mixin in mixins:
371
438
  if self.linkback:
372
439
  mixin.children.append(self)
373
- if mixin._defns and self.bootstrap is None:
374
- self.bootstrap = mixin.bootstrap
375
440
  self.mixins += mixins
376
441
 
377
442
  def _key_error(self, key, possibilities=None):
@@ -391,28 +456,23 @@ class _Ovld:
391
456
  "Note: you can use @ovld(priority=X) to give higher priority to an overload."
392
457
  )
393
458
 
394
- def rename(self, name):
459
+ def rename(self, name, shortname=None):
395
460
  """Rename this Ovld."""
396
461
  self.name = name
397
- self.__name__ = name
462
+ self.shortname = shortname or name
463
+ self.__name__ = shortname
464
+ self.dispatch = bootstrap_dispatch(self, name=self.shortname)
398
465
 
399
466
  def _set_attrs_from(self, fn):
400
467
  """Inherit relevant attributes from the function."""
401
- if self.bootstrap is None:
402
- self.bootstrap = arg0_is_self(fn)
403
-
404
468
  if self.name is None:
405
- self.name = f"{fn.__module__}.{fn.__qualname__}"
406
- self.shortname = fn.__name__
407
- self.__name__ = fn.__name__
408
469
  self.__qualname__ = fn.__qualname__
409
470
  self.__module__ = fn.__module__
471
+ self.rename(f"{fn.__module__}.{fn.__qualname__}", fn.__name__)
410
472
 
411
- def _maybe_rename(self, fn):
412
- if hasattr(fn, "rename"):
413
- return rename_function(fn, f"{self.__name__}.{fn.rename}")
414
- else:
415
- return fn
473
+ def ensure_compiled(self):
474
+ if not self._compiled:
475
+ self.compile()
416
476
 
417
477
  def compile(self):
418
478
  """Finalize this overload.
@@ -428,39 +488,32 @@ class _Ovld:
428
488
  if self not in mixin.children:
429
489
  mixin.lock()
430
490
 
431
- cls = type(self)
432
491
  if self.name is None:
433
492
  self.name = self.__name__ = f"ovld{self.id}"
434
493
 
435
494
  name = self.__name__
436
495
  self.map = MultiTypeMap(name=name, key_error=self._key_error)
437
496
 
438
- # Replace the appropriate functions by their final behavior
439
- for method in dir(cls):
440
- value = getattr(cls, method)
441
- repl = getattr(value, "_replace_by", None)
442
- if repl:
443
- repl = self._maybe_rename(repl)
444
- setattr(cls, method, repl)
445
-
446
- target = self.ocls if self.bootstrap else cls
447
-
448
- anal = ArgumentAnalyzer()
449
- for key, fn in list(self.defns.items()):
450
- anal.add(fn)
451
- self.argument_analysis = anal
452
- dispatch = generate_dispatch(anal)
453
- self._dispatch = dispatch
454
- 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()
455
508
 
456
509
  for key, fn in list(self.defns.items()):
457
510
  self.register_signature(key, fn)
458
511
 
459
512
  self._compiled = True
460
513
 
461
- @_compile_first
462
514
  def resolve(self, *args):
463
515
  """Find the correct method to call for the given arguments."""
516
+ self.ensure_compiled()
464
517
  return self.map[tuple(map(subtler_type, args))]
465
518
 
466
519
  def register_signature(self, sig, orig_fn):
@@ -516,6 +569,8 @@ class _Ovld:
516
569
  self.compile()
517
570
  for child in self.children:
518
571
  child._update()
572
+ if hasattr(self, "dispatch"):
573
+ self.dispatch.__doc__ = self.mkdoc()
519
574
 
520
575
  def copy(self, mixins=[], linkback=False):
521
576
  """Create a copy of this Ovld.
@@ -523,11 +578,7 @@ class _Ovld:
523
578
  New functions can be registered to the copy without affecting the
524
579
  original.
525
580
  """
526
- return _fresh(_Ovld)(
527
- bootstrap=self.bootstrap,
528
- mixins=[self, *mixins],
529
- linkback=linkback,
530
- )
581
+ return Ovld(mixins=[self, *mixins], linkback=linkback)
531
582
 
532
583
  def variant(self, fn=None, priority=0, **kwargs):
533
584
  """Decorator to create a variant of this Ovld.
@@ -542,34 +593,21 @@ class _Ovld:
542
593
  ov.register(fn, priority=priority)
543
594
  return ov
544
595
 
545
- @_compile_first
546
596
  def __get__(self, obj, cls):
547
- if obj is None:
548
- return self
549
- key = self.shortname
550
- rval = obj.__dict__.get(key, None)
551
- if rval is None:
552
- obj.__dict__[key] = rval = self.ocls(self, obj)
553
- return rval
554
-
555
- @_compile_first
556
- def __getitem__(self, t):
557
- if not isinstance(t, tuple):
558
- t = (t,)
559
- return self.map[t]
560
-
561
- @_compile_first
597
+ if not self._compiled:
598
+ self.compile()
599
+ return self.dispatch.__get__(obj, cls)
600
+
562
601
  @_setattrs(rename="dispatch")
563
- def __call__(self, *args): # pragma: no cover
602
+ def __call__(self, *args, **kwargs): # pragma: no cover
564
603
  """Call the overloaded function.
565
604
 
566
605
  This should be replaced by an auto-generated function.
567
606
  """
568
- key = tuple(map(subtler_type, args))
569
- method = self.map[key]
570
- return method(*args)
607
+ if not self._compiled:
608
+ self.compile()
609
+ return self.dispatch(*args, **kwargs)
571
610
 
572
- @_compile_first
573
611
  @_setattrs(rename="next")
574
612
  def next(self, *args):
575
613
  """Call the next matching method after the caller, in terms of priority or specificity."""
@@ -581,65 +619,29 @@ class _Ovld:
581
619
  def __repr__(self):
582
620
  return f"<Ovld {self.name or hex(id(self))}>"
583
621
 
584
- @_compile_first
585
622
  def display_methods(self):
623
+ self.ensure_compiled()
586
624
  self.map.display_methods()
587
625
 
588
- @_compile_first
589
626
  def display_resolution(self, *args, **kwargs):
627
+ self.ensure_compiled()
590
628
  self.map.display_resolution(*args, **kwargs)
591
629
 
592
630
 
593
631
  def is_ovld(x):
594
632
  """Return whether the argument is an ovld function/method."""
595
- return isinstance(x, _Ovld)
596
-
597
-
598
- class OvldCall:
599
- """Context for an Ovld call."""
600
-
601
- def __init__(self, ovld, bind_to):
602
- """Initialize an OvldCall."""
603
- self.ovld = ovld
604
- self.map = ovld.map
605
- self.obj = bind_to
606
-
607
- @property
608
- def __name__(self):
609
- return self.ovld.__name__
610
-
611
- @property
612
- def __doc__(self):
613
- return self.ovld.__doc__
614
-
615
- @property
616
- def __signature__(self):
617
- return self.ovld.__signature__
618
-
619
- def next(self, *args):
620
- """Call the next matching method after the caller, in terms of priority or specificity."""
621
- fr = sys._getframe(1)
622
- key = (fr.f_code, *map(subtler_type, args))
623
- method = self.map[key]
624
- return method(self.obj, *args)
625
-
626
- def resolve(self, *args):
627
- """Find the right method to call for the given arguments."""
628
- return self.map[tuple(map(subtler_type, args))].__get__(self.obj)
629
-
630
- def __call__(self, *args): # pragma: no cover
631
- """Call this overloaded function.
632
-
633
- This should be replaced by an auto-generated function.
634
- """
635
- key = tuple(map(subtler_type, args))
636
- method = self.map[key]
637
- return method(self.obj, *args)
633
+ return isinstance(x, Ovld) or isinstance(
634
+ getattr(x, "__ovld__", False), Ovld
635
+ )
638
636
 
639
637
 
640
- def Ovld(*args, **kwargs):
641
- """Returns an instance of a fresh Ovld class."""
642
- return _fresh(_Ovld)(*args, **kwargs)
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
643
645
 
644
646
 
645
647
  def extend_super(fn):
@@ -666,33 +668,24 @@ class ovld_cls_dict(dict):
666
668
  def __setitem__(self, attr, value):
667
669
  prev = None
668
670
  if attr in self:
669
- prev = self[attr]
670
- if inspect.isfunction(prev):
671
- prev = ovld(prev, fresh=True)
672
- elif not is_ovld(prev): # pragma: no cover
673
- prev = None
671
+ prev = to_ovld(self[attr])
674
672
  elif is_ovld(value) and getattr(value, "_extend_super", False):
675
673
  mixins = []
676
674
  for base in self._bases:
677
675
  if (candidate := getattr(base, attr, None)) is not None:
678
- if is_ovld(candidate) or inspect.isfunction(candidate):
679
- mixins.append(candidate)
676
+ if mixin := to_ovld(candidate):
677
+ mixins.append(mixin)
680
678
  if mixins:
681
679
  prev, *others = mixins
682
- if is_ovld(prev):
683
- prev = prev.copy()
684
- else:
685
- prev = ovld(prev, fresh=True)
680
+ prev = prev.copy()
686
681
  for other in others:
687
- if is_ovld(other):
688
- prev.add_mixins(other)
689
- else:
690
- prev.register(other)
682
+ prev.add_mixins(other)
691
683
  else:
692
684
  prev = None
693
685
 
694
686
  if prev is not None:
695
687
  if is_ovld(value) and prev is not value:
688
+ value = getattr(value, "__ovld__", value)
696
689
  if prev.name is None:
697
690
  prev.rename(value.name)
698
691
  prev.add_mixins(value)
@@ -701,7 +694,9 @@ class ovld_cls_dict(dict):
701
694
  prev.register(value)
702
695
  value = prev
703
696
 
704
- super().__setitem__(attr, value)
697
+ super().__setitem__(
698
+ attr, value.dispatch if isinstance(value, Ovld) else value
699
+ )
705
700
 
706
701
 
707
702
  class OvldMC(type):
@@ -762,12 +757,12 @@ def _find_overload(fn, **kwargs):
762
757
  dispatch = fr.f_locals.get(fn.__name__, None)
763
758
 
764
759
  if dispatch is None:
765
- dispatch = _fresh(_Ovld)(**kwargs)
760
+ dispatch = Ovld(**kwargs)
766
761
  elif not is_ovld(dispatch): # pragma: no cover
767
762
  raise TypeError("@ovld requires Ovld instance")
768
763
  elif kwargs: # pragma: no cover
769
764
  raise TypeError("Cannot configure an overload that already exists")
770
- return dispatch
765
+ return getattr(dispatch, "__ovld__", dispatch)
771
766
 
772
767
 
773
768
  @keyword_decorator
@@ -796,16 +791,16 @@ def ovld(fn, priority=0, fresh=False, **kwargs):
796
791
  ovld so that updates can be propagated. (default: False)
797
792
  """
798
793
  if fresh:
799
- dispatch = _fresh(_Ovld)(**kwargs)
794
+ dispatch = Ovld(**kwargs)
800
795
  else:
801
796
  dispatch = _find_overload(fn, **kwargs)
802
- return dispatch.register(fn, priority=priority)
797
+ dispatch.register(fn, priority=priority)
798
+ return dispatch.dispatch
803
799
 
804
800
 
805
801
  __all__ = [
806
802
  "Ovld",
807
803
  "OvldBase",
808
- "OvldCall",
809
804
  "OvldMC",
810
805
  "extend_super",
811
806
  "is_ovld",
ovld/dependent.py CHANGED
@@ -2,7 +2,6 @@ import inspect
2
2
  import re
3
3
  from collections.abc import Callable as _Callable
4
4
  from collections.abc import Mapping, Sequence
5
- from dataclasses import dataclass
6
5
  from functools import partial
7
6
  from itertools import count
8
7
  from typing import (
@@ -16,6 +15,7 @@ from .types import (
16
15
  Intersection,
17
16
  Order,
18
17
  clsstring,
18
+ get_args,
19
19
  normalize_type,
20
20
  subclasscheck,
21
21
  typeorder,
@@ -28,13 +28,13 @@ def generate_checking_code(typ):
28
28
  if hasattr(typ, "codegen"):
29
29
  return typ.codegen()
30
30
  else:
31
- return CodeGen("isinstance({arg}, {this})", {"this": typ})
31
+ return CodeGen("isinstance({arg}, {this})", this=typ)
32
32
 
33
33
 
34
- @dataclass
35
34
  class CodeGen:
36
- template: str
37
- substitutions: dict
35
+ def __init__(self, template, substitutions={}, **substitutions_kw):
36
+ self.template = template
37
+ self.substitutions = {**substitutions, **substitutions_kw}
38
38
 
39
39
  def mangle(self):
40
40
  renamings = {
@@ -46,10 +46,7 @@ class CodeGen:
46
46
  for k, newk in renamings.items()
47
47
  if k in self.substitutions
48
48
  }
49
- return CodeGen(
50
- template=self.template.format(**renamings),
51
- substitutions=new_subs,
52
- )
49
+ return CodeGen(self.template.format(**renamings), new_subs)
53
50
 
54
51
 
55
52
  def combine(master_template, args):
@@ -65,7 +62,7 @@ def combine(master_template, args):
65
62
  def is_dependent(t):
66
63
  if isinstance(t, DependentType):
67
64
  return True
68
- elif any(is_dependent(subt) for subt in getattr(t, "__args__", ())):
65
+ elif any(is_dependent(subt) for subt in get_args(t)):
69
66
  return True
70
67
  return False
71
68
 
@@ -93,7 +90,7 @@ class DependentType(type):
93
90
  raise NotImplementedError()
94
91
 
95
92
  def codegen(self):
96
- return CodeGen("{this}.check({arg})", {"this": self})
93
+ return CodeGen("{this}.check({arg})", this=self)
97
94
 
98
95
  def __type_order__(self, other):
99
96
  if isinstance(other, DependentType):
@@ -145,6 +142,11 @@ class ParametrizedDependentType(DependentType):
145
142
  self.default_bound(*parameters) if bound is None else bound
146
143
  )
147
144
  self.__args__ = self.parameters = parameters
145
+ self.__origin__ = None
146
+ self.__post_init__()
147
+
148
+ def __post_init__(self):
149
+ pass
148
150
 
149
151
  @property
150
152
  def parameter(self):
@@ -184,10 +186,7 @@ class ParametrizedDependentType(DependentType):
184
186
 
185
187
  class FuncDependentType(ParametrizedDependentType):
186
188
  def default_bound(self, *_):
187
- fn = type(self).func
188
- return normalize_type(
189
- list(inspect.signature(fn).parameters.values())[0].annotation, fn
190
- )
189
+ return self._default_bound
191
190
 
192
191
  def __lt__(self, other):
193
192
  if len(self.parameters) != len(other.parameters):
@@ -209,13 +208,40 @@ class FuncDependentType(ParametrizedDependentType):
209
208
  def dependent_check(fn=None, bound_is_name=False):
210
209
  if fn is None:
211
210
  return partial(dependent_check, bound_is_name=bound_is_name)
212
- t = type(
213
- fn.__name__,
214
- (FuncDependentType,),
215
- {"func": fn, "bound_is_name": bound_is_name},
216
- )
217
- if len(inspect.signature(fn).parameters) == 1:
218
- t = t()
211
+
212
+ if isinstance(fn, type):
213
+ params = inspect.signature(fn.check).parameters
214
+ bound = normalize_type(
215
+ list(inspect.signature(fn.check).parameters.values())[1].annotation,
216
+ fn.check,
217
+ )
218
+ t = type(
219
+ fn.__name__,
220
+ (FuncDependentType,),
221
+ {
222
+ "bound_is_name": bound_is_name,
223
+ "_default_bound": bound,
224
+ **vars(fn),
225
+ },
226
+ )
227
+
228
+ else:
229
+ params = inspect.signature(fn).parameters
230
+ bound = normalize_type(
231
+ list(inspect.signature(fn).parameters.values())[0].annotation, fn
232
+ )
233
+ t = type(
234
+ fn.__name__,
235
+ (FuncDependentType,),
236
+ {
237
+ "func": fn,
238
+ "bound_is_name": bound_is_name,
239
+ "_default_bound": bound,
240
+ },
241
+ )
242
+ if len(params) == 1:
243
+ t = t()
244
+
219
245
  return t
220
246
 
221
247
 
@@ -237,9 +263,9 @@ class Equals(ParametrizedDependentType):
237
263
 
238
264
  def codegen(self):
239
265
  if len(self.parameters) == 1:
240
- return CodeGen("({arg} == {p})", {"p": self.parameter})
266
+ return CodeGen("({arg} == {p})", p=self.parameter)
241
267
  else:
242
- return CodeGen("({arg} in {ps})", {"ps": self.parameters})
268
+ return CodeGen("({arg} in {ps})", ps=self.parameters)
243
269
 
244
270
 
245
271
  class ProductType(ParametrizedDependentType):
@@ -327,8 +353,15 @@ def EndsWith(value: str, suffix):
327
353
 
328
354
 
329
355
  @dependent_check
330
- def Regexp(value: str, regexp):
331
- return bool(re.search(pattern=regexp, string=value))
356
+ class Regexp:
357
+ def __post_init__(self):
358
+ self.rx = re.compile(self.parameter)
359
+
360
+ def check(self, value: str):
361
+ return bool(self.rx.search(value))
362
+
363
+ def codegen(self):
364
+ return CodeGen("bool({rx}.search({arg}))", rx=self.rx)
332
365
 
333
366
 
334
367
  class Dependent:
ovld/mro.py CHANGED
@@ -3,6 +3,8 @@ from enum import Enum
3
3
  from graphlib import TopologicalSorter
4
4
  from typing import get_args, get_origin
5
5
 
6
+ from .utils import UnionTypes
7
+
6
8
 
7
9
  class Order(Enum):
8
10
  LESS = -1
@@ -121,6 +123,9 @@ def subclasscheck(t1, t2):
121
123
  ):
122
124
  return result
123
125
 
126
+ if t2 in UnionTypes:
127
+ return isinstance(t1, t2)
128
+
124
129
  o1 = get_origin(t1)
125
130
  o2 = get_origin(t2)
126
131
 
ovld/py.typed ADDED
File without changes
ovld/recode.py CHANGED
@@ -1,14 +1,13 @@
1
1
  import ast
2
2
  import inspect
3
3
  import linecache
4
- import re
5
4
  import textwrap
6
5
  from ast import _splitlines_no_ff as splitlines
7
6
  from functools import reduce
8
7
  from itertools import count
9
8
  from types import CodeType, FunctionType
10
9
 
11
- from .utils import MISSING, Unusable, UsageError, subtler_type
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,20 +17,8 @@ call_next = Unusable(
18
17
  )
19
18
 
20
19
 
21
- dispatch_template = """
22
- def __DISPATCH__(self, {args}):
23
- {body}
24
- """
25
-
26
-
27
- call_template = """
28
- method = self.map[({lookup})]
29
- return method({posargs})
30
- """
31
-
32
-
33
20
  def instantiate_code(symbol, code, inject={}):
34
- virtual_file = f"<ovld{hash(code)}>"
21
+ virtual_file = f"<ovld:{abs(hash(code)):x}>"
35
22
  linecache.cache[virtual_file] = (None, None, splitlines(code), virtual_file)
36
23
  code = compile(source=code, filename=virtual_file, mode="exec")
37
24
  glb = {**inject}
@@ -51,36 +38,22 @@ def instantiate_code(symbol, code, inject={}):
51
38
  # return rval
52
39
 
53
40
 
54
- class NameDatabase:
55
- def __init__(self, default_name):
56
- self.default_name = default_name
57
- self.count = count()
58
- self.variables = {}
59
- self.names = {}
60
- self.registered = set()
61
-
62
- def register(self, name):
63
- self.registered.add(name)
64
-
65
- def __getitem__(self, value):
66
- if isinstance(value, (int, float, str)):
67
- return repr(value)
68
- if id(value) in self.names:
69
- return self.names[id(value)]
70
- name = orig_name = getattr(value, "__name__", self.default_name)
71
- if not re.match(string=name, pattern=r"[a-zA-Z_][a-zA-Z0-9_]+"):
72
- name = self.default_name
73
- i = 1
74
- while name in self.registered:
75
- name = f"{orig_name}{i}"
76
- i += 1
77
- self.variables[name] = value
78
- self.names[id(value)] = name
79
- self.registered.add(name)
80
- return name
81
-
82
-
83
- def generate_dispatch(arganal):
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):
84
57
  def join(li, sep=", ", trail=False):
85
58
  li = [x for x in li if x]
86
59
  rval = sep.join(li)
@@ -88,16 +61,23 @@ def generate_dispatch(arganal):
88
61
  rval += ","
89
62
  return rval
90
63
 
91
- spr, spo, pr, po, kr, ko = arganal.compile()
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
92
72
 
93
73
  inits = set()
94
74
 
95
75
  kwargsstar = ""
96
76
  targsstar = ""
97
77
 
98
- args = []
78
+ args = ["self" if arganal.is_method else ""]
99
79
  body = [""]
100
- posargs = ["self.obj" if arganal.is_method else ""]
80
+ posargs = ["self" if arganal.is_method else ""]
101
81
  lookup = []
102
82
 
103
83
  i = 0
@@ -109,6 +89,8 @@ def generate_dispatch(arganal):
109
89
  for name in spr + spo + pr + po + kr:
110
90
  ndb.register(name)
111
91
 
92
+ mv = ndb.gensym(desired_name="method")
93
+
112
94
  for name in spr + spo:
113
95
  if name in spr:
114
96
  args.append(name)
@@ -118,7 +100,7 @@ def generate_dispatch(arganal):
118
100
  lookup.append(f"{lookup_for(i)}({name})")
119
101
  i += 1
120
102
 
121
- if len(po) <= 1:
103
+ if len(po) <= 1 and (spr or spo):
122
104
  # If there are more than one non-strictly positional optional arguments,
123
105
  # then all positional arguments are strictly positional, because if e.g.
124
106
  # x and y are optional we want x==MISSING to imply that y==MISSING, but
@@ -161,6 +143,7 @@ def generate_dispatch(arganal):
161
143
  fullcall = call_template.format(
162
144
  lookup=join(lookup, trail=True),
163
145
  posargs=join(posargs),
146
+ mvar=mv,
164
147
  )
165
148
 
166
149
  calls = []
@@ -170,19 +153,21 @@ def generate_dispatch(arganal):
170
153
  call = call_template.format(
171
154
  lookup=join(lookup[: req + i], trail=True),
172
155
  posargs=join(posargs[: req + i + 1]),
156
+ mvar=mv,
173
157
  )
174
- call = textwrap.indent(call, " ")
158
+ call = textwrap.indent(call, " ")
175
159
  calls.append(f"\nif {arg} is MISSING:{call}")
176
160
  calls.append(fullcall)
177
161
 
178
- lines = [*inits, *body, textwrap.indent("".join(calls), " ")]
162
+ lines = [*inits, *body, textwrap.indent("".join(calls), " ")]
179
163
  code = dispatch_template.format(
180
164
  args=join(args),
181
- body=join(lines, sep="\n ").lstrip(),
165
+ body=join(lines, sep="\n ").lstrip(),
182
166
  )
183
- return instantiate_code(
184
- "__DISPATCH__", code, inject={"MISSING": MISSING, **ndb.variables}
167
+ wr = instantiate_code(
168
+ "__WRAP_DISPATCH__", code, inject={"MISSING": MISSING, **ndb.variables}
185
169
  )
170
+ return wr(ov)
186
171
 
187
172
 
188
173
  def generate_dependent_dispatch(tup, handlers, next_call, slf, name, err, nerr):
@@ -252,6 +237,9 @@ def generate_dependent_dispatch(tup, handlers, next_call, slf, name, err, nerr):
252
237
  conj = "True"
253
238
  conjs.append(conj)
254
239
 
240
+ if len(handlers) == 1:
241
+ exclusive = True
242
+
255
243
  argspec = ", ".join(argname(x) for x in tup)
256
244
  argcall = ", ".join(argprovide(x) for x in tup)
257
245
 
@@ -380,12 +368,19 @@ def rename_function(fn, newname):
380
368
 
381
369
  class NameConverter(ast.NodeTransformer):
382
370
  def __init__(
383
- self, anal, recurse_sym, call_next_sym, ovld_mangled, code_mangled
371
+ self,
372
+ anal,
373
+ recurse_sym,
374
+ call_next_sym,
375
+ ovld_mangled,
376
+ map_mangled,
377
+ code_mangled,
384
378
  ):
385
379
  self.analysis = anal
386
380
  self.recurse_sym = recurse_sym
387
381
  self.call_next_sym = call_next_sym
388
382
  self.ovld_mangled = ovld_mangled
383
+ self.map_mangled = map_mangled
389
384
  self.code_mangled = code_mangled
390
385
  self.count = count()
391
386
 
@@ -450,14 +445,7 @@ class NameConverter(ast.NodeTransformer):
450
445
  if cn:
451
446
  type_parts.insert(0, ast.Name(id=self.code_mangled, ctx=ast.Load()))
452
447
  method = ast.Subscript(
453
- value=ast.NamedExpr(
454
- target=ast.Name(id=f"{tmp}M", ctx=ast.Store()),
455
- value=ast.Attribute(
456
- value=ast.Name(id=self.ovld_mangled, ctx=ast.Load()),
457
- attr="map",
458
- ctx=ast.Load(),
459
- ),
460
- ),
448
+ value=ast.Name(id=self.map_mangled, ctx=ast.Load()),
461
449
  slice=ast.Tuple(
462
450
  elts=type_parts,
463
451
  ctx=ast.Load(),
@@ -465,19 +453,14 @@ class NameConverter(ast.NodeTransformer):
465
453
  ctx=ast.Load(),
466
454
  )
467
455
  if self.analysis.is_method:
468
- method = ast.Call(
469
- func=ast.Attribute(
470
- value=method,
471
- attr="__get__",
472
- ctx=ast.Load(),
473
- ),
474
- args=[ast.Name(id="self", ctx=ast.Load())],
475
- keywords=[],
476
- )
456
+ selfarg = [ast.Name(id="self", ctx=ast.Load())]
457
+ else:
458
+ selfarg = []
477
459
 
478
460
  new_node = ast.Call(
479
461
  func=method,
480
- args=[
462
+ args=selfarg
463
+ + [
481
464
  ast.Name(id=f"{tmp}{i}", ctx=ast.Load())
482
465
  for i, arg in enumerate(node.args)
483
466
  ],
@@ -510,7 +493,10 @@ def adapt_function(fn, ovld, newname):
510
493
  """Create a copy of the function with a different name."""
511
494
  rec_syms = list(
512
495
  _search_names(
513
- fn.__code__, (recurse, ovld), fn.__globals__, fn.__closure__
496
+ fn.__code__,
497
+ (recurse, ovld, ovld.dispatch),
498
+ fn.__globals__,
499
+ fn.__closure__,
514
500
  )
515
501
  )
516
502
  cn_syms = list(
@@ -552,6 +538,7 @@ def closure_wrap(tree, fname, names):
552
538
 
553
539
  def recode(fn, ovld, recurse_sym, call_next_sym, newname):
554
540
  ovld_mangled = f"___OVLD{ovld.id}"
541
+ map_mangled = f"___MAP{ovld.id}"
555
542
  code_mangled = f"___CODE{next(_current)}"
556
543
  try:
557
544
  src = inspect.getsource(fn)
@@ -568,6 +555,7 @@ def recode(fn, ovld, recurse_sym, call_next_sym, newname):
568
555
  recurse_sym=recurse_sym,
569
556
  call_next_sym=call_next_sym,
570
557
  ovld_mangled=ovld_mangled,
558
+ map_mangled=map_mangled,
571
559
  code_mangled=code_mangled,
572
560
  ).visit(tree)
573
561
  new.body[0].decorator_list = []
@@ -592,6 +580,7 @@ def recode(fn, ovld, recurse_sym, call_next_sym, newname):
592
580
  new_fn.__annotations__ = fn.__annotations__
593
581
  new_fn = rename_function(new_fn, newname)
594
582
  new_fn.__globals__["__SUBTLER_TYPE"] = subtler_type
595
- new_fn.__globals__[ovld_mangled] = ovld
583
+ new_fn.__globals__[ovld_mangled] = ovld.dispatch
584
+ new_fn.__globals__[map_mangled] = ovld.map
596
585
  new_fn.__globals__[code_mangled] = new_fn.__code__
597
586
  return new_fn
ovld/types.py CHANGED
@@ -3,16 +3,18 @@ import sys
3
3
  import typing
4
4
  from dataclasses import dataclass
5
5
  from functools import partial
6
- from typing import Protocol, runtime_checkable
6
+ from typing import Protocol, get_args, runtime_checkable
7
7
 
8
8
  from .mro import Order, TypeRelationship, subclasscheck, typeorder
9
9
  from .typemap import TypeMap
10
- from .utils import UsageError, clsstring
10
+ from .utils import UnionType, UnionTypes, UsageError, clsstring
11
11
 
12
- try:
13
- from types import UnionType
14
- except ImportError: # pragma: no cover
15
- UnionType = None
12
+
13
+ def get_args(tp):
14
+ args = getattr(tp, "__args__", None)
15
+ if not isinstance(args, tuple):
16
+ args = ()
17
+ return args
16
18
 
17
19
 
18
20
  class TypeNormalizer:
@@ -37,6 +39,8 @@ class TypeNormalizer:
37
39
  t = object
38
40
  elif t is inspect._empty:
39
41
  t = object
42
+ elif t in UnionTypes:
43
+ return type[t]
40
44
  elif isinstance(t, typing._AnnotatedAlias):
41
45
  t = t.__origin__
42
46
 
@@ -82,7 +86,7 @@ class MetaMC(type):
82
86
  return super().__new__(T, name, (), {"_handler": handler})
83
87
 
84
88
  def __init__(cls, name, handler):
85
- cls.__args__ = getattr(handler, "__args__", ())
89
+ cls.__args__ = get_args(handler)
86
90
 
87
91
  def codegen(cls):
88
92
  return cls._handler.codegen()
ovld/utils.py CHANGED
@@ -1,7 +1,18 @@
1
1
  """Miscellaneous utilities."""
2
2
 
3
3
  import functools
4
+ import re
4
5
  import typing
6
+ from itertools import count
7
+
8
+ try:
9
+ from types import UnionType
10
+
11
+ UnionTypes = (type(typing.Union[int, str]), UnionType)
12
+
13
+ except ImportError: # pragma: no cover
14
+ UnionType = None
15
+ UnionTypes = (type(typing.Union[int, str]),)
5
16
 
6
17
 
7
18
  class Named:
@@ -88,6 +99,8 @@ def clsstring(cls):
88
99
  def subtler_type(obj):
89
100
  if isinstance(obj, GenericAlias):
90
101
  return type[obj]
102
+ elif isinstance(obj, UnionTypes):
103
+ return type[obj]
91
104
  elif obj is typing.Any:
92
105
  return type[object]
93
106
  elif isinstance(obj, type):
@@ -96,6 +109,40 @@ def subtler_type(obj):
96
109
  return type(obj)
97
110
 
98
111
 
112
+ class NameDatabase:
113
+ def __init__(self, default_name):
114
+ self.default_name = default_name
115
+ self.count = count()
116
+ self.variables = {}
117
+ self.names = {}
118
+ self.registered = set()
119
+
120
+ def register(self, name):
121
+ self.registered.add(name)
122
+
123
+ def gensym(self, desired_name):
124
+ i = 1
125
+ name = desired_name
126
+ while name in self.registered:
127
+ name = f"{desired_name}{i}"
128
+ i += 1
129
+ self.registered.add(name)
130
+ return name
131
+
132
+ def __getitem__(self, value):
133
+ if isinstance(value, (int, float, str)):
134
+ return repr(value)
135
+ if id(value) in self.names:
136
+ return self.names[id(value)]
137
+ name = getattr(value, "__name__", self.default_name)
138
+ if not re.match(string=name, pattern=r"[a-zA-Z_][a-zA-Z0-9_]+"):
139
+ name = self.default_name
140
+ name = self.gensym(name)
141
+ self.variables[name] = value
142
+ self.names[id(value)] = name
143
+ return name
144
+
145
+
99
146
  __all__ = [
100
147
  "BOOTSTRAP",
101
148
  "MISSING",
ovld/version.py CHANGED
@@ -1 +1 @@
1
- version = "0.4.4"
1
+ version = "0.4.6"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: ovld
3
- Version: 0.4.4
3
+ Version: 0.4.6
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/
@@ -201,16 +201,17 @@ assert Two().f("s") == "a string"
201
201
 
202
202
  # Benchmarks
203
203
 
204
- `ovld` is pretty fast: the overhead is comparable to `isinstance` or `match`, and only 2-3x slower when dispatching on `Literal` types. Compared to other multiple dispatch libraries, it is 1.5x to 100x faster.
204
+ `ovld` is pretty fast: the overhead is comparable to `isinstance` or `match`, and only 2-3x slower when dispatching on `Literal` types. Compared to other multiple dispatch libraries, it has 1.5x to 100x less overhead.
205
205
 
206
206
  Time relative to the fastest implementation (1.00) (lower is better).
207
207
 
208
- | Bench | custom | [ovld](https://github.com/breuleux/ovld) | [plum](https://github.com/beartype/plum) | [multim](https://github.com/coady/multimethod) | [multid](https://github.com/mrocklin/multipledispatch/) | [runtype](https://github.com/erezsh/runtype) | [fastcore](https://github.com/fastai/fastcore) | [singled](https://docs.python.org/3/library/functools.html#functools.singledispatch) |
208
+ | Benchmark | custom | [ovld](https://github.com/breuleux/ovld) | [plum](https://github.com/beartype/plum) | [multim](https://github.com/coady/multimethod) | [multid](https://github.com/mrocklin/multipledispatch/) | [runtype](https://github.com/erezsh/runtype) | [fastcore](https://github.com/fastai/fastcore) | [sd](https://docs.python.org/3/library/functools.html#functools.singledispatch) |
209
209
  | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
210
- |[trivial](https://github.com/breuleux/ovld/tree/master/benchmarks/test_trivial.py)|1.14|1.00|2.53|3.64|1.61|1.86|41.31|1.54|
211
- |[add](https://github.com/breuleux/ovld/tree/master/benchmarks/test_add.py)|1.01|1.00|3.46|4.83|2.21|2.66|56.08|x|
212
- |[multer](https://github.com/breuleux/ovld/tree/master/benchmarks/test_multer.py)|1.00|1.06|9.79|4.11|7.19|1.89|40.37|6.34|
213
- |[ast](https://github.com/breuleux/ovld/tree/master/benchmarks/test_ast.py)|1.00|1.06|23.07|3.04|1.68|1.87|29.11|1.63|
214
- |[calc](https://github.com/breuleux/ovld/tree/master/benchmarks/test_calc.py)|1.00|1.96|80.00|43.21|x|x|x|x|
215
- |[fib](https://github.com/breuleux/ovld/tree/master/benchmarks/test_fib.py)|1.00|3.58|438.97|123.58|x|x|x|x|
216
- |[tweak](https://github.com/breuleux/ovld/tree/master/benchmarks/test_tweaknum.py)|1.00|2.59|x|x|x|x|x|x|
210
+ |[trivial](https://github.com/breuleux/ovld/tree/master/benchmarks/test_trivial.py)|1.45|1.00|3.32|4.63|2.04|2.41|51.93|1.91|
211
+ |[multer](https://github.com/breuleux/ovld/tree/master/benchmarks/test_multer.py)|1.13|1.00|11.05|4.53|8.31|2.19|46.74|7.32|
212
+ |[add](https://github.com/breuleux/ovld/tree/master/benchmarks/test_add.py)|1.08|1.00|3.73|5.21|2.37|2.79|59.31|x|
213
+ |[ast](https://github.com/breuleux/ovld/tree/master/benchmarks/test_ast.py)|1.00|1.08|23.14|3.09|1.68|1.91|28.39|1.66|
214
+ |[calc](https://github.com/breuleux/ovld/tree/master/benchmarks/test_calc.py)|1.00|1.23|54.61|29.32|x|x|x|x|
215
+ |[regexp](https://github.com/breuleux/ovld/tree/master/benchmarks/test_regexp.py)|1.00|1.87|19.18|x|x|x|x|x|
216
+ |[fib](https://github.com/breuleux/ovld/tree/master/benchmarks/test_fib.py)|1.00|3.30|444.31|125.77|x|x|x|x|
217
+ |[tweaknum](https://github.com/breuleux/ovld/tree/master/benchmarks/test_tweaknum.py)|1.00|2.09|x|x|x|x|x|x|
@@ -0,0 +1,15 @@
1
+ ovld/__init__.py,sha256=IVzs4_9skMa_UGZsGT7Ra_dIxj7KKNlV77XxlqcS6ow,1446
2
+ ovld/abc.py,sha256=4qpZyYwI8dWgY1Oiv5FhdKg2uzNcyWxIpGmGJVcjXrs,1177
3
+ ovld/core.py,sha256=MtRcJNhMwsix2Y7zP1fQS-taCy_t0bqkjHQdLF8WE8E,25719
4
+ ovld/dependent.py,sha256=M85EJPRGNnEpmsr3tEX1U-1di1cxQb5-0oQtLgvfRoA,10183
5
+ ovld/mro.py,sha256=D9KY3KEFnWL_SXZqHsVKNgRM1E0d8TPPILVZ3SxdUb4,4643
6
+ ovld/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ ovld/recode.py,sha256=bwsdM-TPpSQEmJhv8G30yDLrF8_I5wyTXpITMCC1W9k,17961
8
+ ovld/typemap.py,sha256=PH5dy8ddVCcqj2TkQbgsM7fmCdHsJT9WGXFPn4JZsCA,13131
9
+ ovld/types.py,sha256=lvOl1fldIqCJM-TvvdM2nMvAYDgcA45awfpkUB6kKpo,12984
10
+ ovld/utils.py,sha256=k-cVMbJigtdeuD5_JEYtq1a6fXhmUGqLfBu8YxYd5VY,3395
11
+ ovld/version.py,sha256=DWNNbWBv-hyqVy76bKbnHWaDUbqL0BOtdkPSV_88dx0,18
12
+ ovld-0.4.6.dist-info/METADATA,sha256=mXEwvqopBdpksDEKETVZkp-airCb48yt4AWZIQ2r2wU,7833
13
+ ovld-0.4.6.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
14
+ ovld-0.4.6.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
15
+ ovld-0.4.6.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- ovld/__init__.py,sha256=GEhOdG64GPUY5Y5BMnaik7UMgcq12mWVhxN9quIonYw,1476
2
- ovld/abc.py,sha256=4qpZyYwI8dWgY1Oiv5FhdKg2uzNcyWxIpGmGJVcjXrs,1177
3
- ovld/core.py,sha256=vzrYgUdagsQH6gbw32kzYzd-MvnCE3O9LjUyc-vVk1M,25366
4
- ovld/dependent.py,sha256=vvWjjOHNtw8PegZVDj1fRKpyp6SCg1WdOWoXNxZSVrk,9288
5
- ovld/mro.py,sha256=0cJK_v-POCiuwjluwf8fWeQ3G-2chEUv3KYe57GC61Q,4552
6
- ovld/recode.py,sha256=NWAM0qyOQqXgVCZxtmhtnkygWe-NV5FRwdXGtqikGTg,18494
7
- ovld/typemap.py,sha256=PH5dy8ddVCcqj2TkQbgsM7fmCdHsJT9WGXFPn4JZsCA,13131
8
- ovld/types.py,sha256=EZNv8pThbo47KBULYM3R0R3sM2C5LC4DWraXZImkiNs,12877
9
- ovld/utils.py,sha256=BsE7MmW5sfnO4Ym8hcEL8JNZZ1JIX_pgVRAAidh_8_w,2085
10
- ovld/version.py,sha256=Qk6wsgzl7cSMWWZ902odoQ-wBV5Fda2ShX82NN7uHzM,18
11
- ovld-0.4.4.dist-info/METADATA,sha256=wUsEDZyoQPooCN7P7q4MyObKAeSjg7DqpR-vRne5BW8,7713
12
- ovld-0.4.4.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
13
- ovld-0.4.4.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
14
- ovld-0.4.4.dist-info/RECORD,,
File without changes