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/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
- from .typemap import MultiTypeMap, is_type_of_type
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
- @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)
@@ -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 clsstring(cls):
153
- if cls is object:
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 repr(cls)
231
+ return clsstring(cls)
170
232
 
171
233
 
172
234
  def sigstring(types):
173
- return ", ".join(map(clsstring, types))
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 _Ovld:
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
- @property
338
- def __doc__(self):
339
- if not self._compiled:
340
- 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
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 __signature__(self):
363
- if not self._compiled:
364
- self.compile()
419
+ def __doc__(self):
420
+ self.ensure_compiled()
421
+ return self.mkdoc()
365
422
 
366
- sig = inspect.signature(self._dispatch)
367
- if not self.argument_analysis.is_method:
368
- sig = inspect.Signature(
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.__name__ = name
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 _maybe_rename(self, fn):
424
- if hasattr(fn, "rename"):
425
- return rename_function(fn, f"{self.__name__}.{fn.rename}")
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
- # Replace the appropriate functions by their final behavior
451
- for method in dir(cls):
452
- value = getattr(cls, method)
453
- repl = getattr(value, "_replace_by", None)
454
- if repl:
455
- repl = self._maybe_rename(repl)
456
- setattr(cls, method, repl)
457
-
458
- target = self.ocls if self.bootstrap else cls
459
-
460
- anal = ArgumentAnalyzer()
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
- return self.map[tuple(map(self.map.transform, args))]
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 _fresh(_Ovld)(
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 obj is None:
560
- return self
561
- key = self.shortname
562
- rval = obj.__dict__.get(key, None)
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
- key = tuple(map(self.map.transform, args))
581
- method = self.map[key]
582
- return method(*args)
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(self.map.transform, args))
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, _Ovld)
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 Ovld(*args, **kwargs):
653
- """Returns an instance of a fresh Ovld class."""
654
- 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
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 is_ovld(candidate) or inspect.isfunction(candidate):
691
- mixins.append(candidate)
676
+ if mixin := to_ovld(candidate):
677
+ mixins.append(mixin)
692
678
  if mixins:
693
679
  prev, *others = mixins
694
- if is_ovld(prev):
695
- prev = prev.copy()
696
- else:
697
- prev = ovld(prev, fresh=True)
680
+ prev = prev.copy()
698
681
  for other in others:
699
- if is_ovld(other):
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__(attr, value)
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 = _fresh(_Ovld)(**kwargs)
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 = _fresh(_Ovld)(**kwargs)
794
+ dispatch = Ovld(**kwargs)
812
795
  else:
813
796
  dispatch = _find_overload(fn, **kwargs)
814
- return dispatch.register(fn, priority=priority)
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",