ovld 0.4.1__tar.gz → 0.4.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ovld-0.4.2/.bsync-snap-20240916114026.355340 +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/PKG-INFO +1 -1
- {ovld-0.4.1 → ovld-0.4.2}/pyproject.toml +1 -1
- ovld-0.4.2/reddit.md +54 -0
- ovld-0.4.2/reddit.py +24 -0
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/core.py +12 -3
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/recode.py +7 -5
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/typemap.py +48 -21
- ovld-0.4.2/src/ovld/version.py +1 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_ovld.py +59 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_typemap.py +1 -1
- {ovld-0.4.1 → ovld-0.4.2}/todo.q +1 -0
- {ovld-0.4.1 → ovld-0.4.2}/uv.lock +1 -1
- ovld-0.4.1/src/ovld/version.py +0 -1
- {ovld-0.4.1 → ovld-0.4.2}/.bsync-snap-20220324145902.852076 +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/.envrc +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/.github/workflows/python-package.yml +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/.gitignore +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/.python-version +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/.readthedocs.yaml +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/LICENSE +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/README.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/add.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/anal.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/bench/requirements.txt +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/bench.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchd.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/__init__.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/common.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/test_add.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/test_ast.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/test_calc.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/test_fib.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/test_multer.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/test_trivial.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/benchmarks/test_tweaknum.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/cloz.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/data.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/didi.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/docs/compare.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/docs/dependent.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/docs/features.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/docs/index.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/docs/types.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/docs/usage.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/doo.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/edges.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/explore.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/facto.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/gen_comparison_table.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/gentest.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/hello.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/hntest.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/klos.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/mkdocs.yml +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/nxt.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.0-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.0.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.1-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.1.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.2-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.2.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.3-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.3.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.4-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.4.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.5-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.5.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.6-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.6.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.7-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.7.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.8-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.8.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.9-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.1.9.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.0-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.0.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.1-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.1.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.10-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.10.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.11-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.11.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.2-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.2.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.3-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.3.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.4-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.4.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.5-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.5.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.6-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.6.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.7-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.7.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.8-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.8.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.9-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.2.9.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.0-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.0.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.1-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.1.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.2-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.2.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.3-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.3.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.4-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.4.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.5-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.5.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.8-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.8.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.9-py3-none-any.whl +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/old-dist/ovld-0.3.9.tar.gz +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/one_two_three.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/outoftheway.toml +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/__init__.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/dependent.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/mro.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/types.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/src/ovld/utils.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/stuff.md +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tb.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tensor.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/__init__.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/modules/gingerbread.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_dependent.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_examples.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_global.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_mro.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_ovld/test_display.txt +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_ovld/test_doc.txt +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_ovld/test_doc2.txt +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_ovld/test_method_doc.txt +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_types.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/tests/test_utils.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/toot.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/typo.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/world.yaml +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/x.py +0 -0
- {ovld-0.4.1 → ovld-0.4.2}/zaggo +0 -0
Binary file
|
ovld-0.4.2/reddit.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
## What My Project Does
|
2
|
+
|
3
|
+
`ovld` implements multiple dispatch in Python. This lets you define multiple versions of the same function with different type signatures.
|
4
|
+
|
5
|
+
For example:
|
6
|
+
|
7
|
+
```python
|
8
|
+
import math
|
9
|
+
from typing import Literal
|
10
|
+
from ovld import ovld
|
11
|
+
|
12
|
+
@ovld
|
13
|
+
def div(x: int, y: int):
|
14
|
+
return x / y
|
15
|
+
|
16
|
+
@ovld
|
17
|
+
def div(x: str, y: str):
|
18
|
+
return f"{x}/{y}"
|
19
|
+
|
20
|
+
@ovld
|
21
|
+
def div(x: int, y: Literal[0]):
|
22
|
+
return math.inf
|
23
|
+
|
24
|
+
assert div(8, 2) == 4
|
25
|
+
assert div("/home", "user") == "/home/user"
|
26
|
+
assert div(10, 0) == math.inf
|
27
|
+
```
|
28
|
+
|
29
|
+
|
30
|
+
## Target Audience
|
31
|
+
|
32
|
+
Ovld is pretty generally applicable: multiple dispatch is a central feature of several programming languages, e.g. Julia. I find it particularly useful when doing work on complex heterogeneous data structures, for instance walking an AST, serializing/deserializing data, generating HTML representations of data, etc.
|
33
|
+
|
34
|
+
|
35
|
+
## Features
|
36
|
+
|
37
|
+
* Wide range of supported annotations: normal types, protocols, Union, Literal, [custom types](https://ovld.readthedocs.io/en/latest/types/#defining-new-types) such as HasMethod, Intersection and arbitrary user-defined types.
|
38
|
+
* Support for [dependent types](https://ovld.readthedocs.io/en/latest/dependent/), by which I mean "types" that depend on the values of the arguments. For example you can easily implement a `Regexp[regex]` type that matches string arguments based on regular expressions, or a type that only matches 2x2 torch.Tensor with int8 dtype.
|
39
|
+
* Dispatch on keyword arguments (with a few limitations).
|
40
|
+
* Define numeric priority levels for disambiguation.
|
41
|
+
* Define [variants](https://ovld.readthedocs.io/en/latest/usage/#variants) of existing functions (copies of existing overloads with additional functionality)
|
42
|
+
* Special `recurse()` function for recursive calls that also work with variants.
|
43
|
+
* Special `call_next()` function to call the next dispatch.
|
44
|
+
|
45
|
+
|
46
|
+
## Comparison
|
47
|
+
|
48
|
+
There already exist a few multiple dispatch libraries: plum, multimethod, multipledispatch, runtype, fastcore, and the builtin functools.singledispatch (single argument).
|
49
|
+
|
50
|
+
Ovld is faster than all of them. From 1.5x to 100x faster depending on use case, and in the ballpark of isinstance/match. It is also generally more featureful: no other library supports dispatch on keyword arguments, and only a few support `Literal` annotations, but with massive performance penalties.
|
51
|
+
|
52
|
+
You can also do many things ovld does with the `match` statement, except you cannot extend existing `match` statements with more signatures.
|
53
|
+
|
54
|
+
[Whole comparison section, with benchmarks, can be found here.](https://ovld.readthedocs.io/en/latest/compare/)
|
ovld-0.4.2/reddit.py
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
import math
|
2
|
+
from typing import Literal
|
3
|
+
|
4
|
+
from ovld import ovld
|
5
|
+
|
6
|
+
|
7
|
+
@ovld
|
8
|
+
def div(x: int, y: int):
|
9
|
+
return x / y
|
10
|
+
|
11
|
+
|
12
|
+
@ovld
|
13
|
+
def div(x: str, y: str):
|
14
|
+
return f"{x}/{y}"
|
15
|
+
|
16
|
+
|
17
|
+
@ovld
|
18
|
+
def div(x: int, y: Literal[0]):
|
19
|
+
return math.inf
|
20
|
+
|
21
|
+
|
22
|
+
assert div(8, 2) == 4
|
23
|
+
assert div("/home", "user") == "/home/user"
|
24
|
+
assert div(10, 0) == math.inf
|
@@ -85,6 +85,7 @@ class Signature:
|
|
85
85
|
req_names: frozenset
|
86
86
|
vararg: bool
|
87
87
|
priority: float
|
88
|
+
tiebreak: int = 0
|
88
89
|
is_method: bool = False
|
89
90
|
arginfo: list[Arginfo] = field(
|
90
91
|
default_factory=list, hash=False, compare=False
|
@@ -393,8 +394,8 @@ class _Ovld:
|
|
393
394
|
)
|
394
395
|
else:
|
395
396
|
hlp = ""
|
396
|
-
for
|
397
|
-
hlp += f"* {
|
397
|
+
for c in possibilities:
|
398
|
+
hlp += f"* {c.handler.__name__} (priority: {c.priority}, specificity: {list(c.specificity)})\n"
|
398
399
|
return TypeError(
|
399
400
|
f"Ambiguous resolution in {self} for"
|
400
401
|
f" argument types [{typenames}]\n"
|
@@ -503,7 +504,15 @@ class _Ovld:
|
|
503
504
|
raise TypeError(
|
504
505
|
f"There is already a method for {sigstring(sig.types)}"
|
505
506
|
)
|
506
|
-
|
507
|
+
|
508
|
+
def _set(sig, fn):
|
509
|
+
if sig in self._defns:
|
510
|
+
# Push down the existing handler with a lower tiebreak
|
511
|
+
msig = replace(sig, tiebreak=sig.tiebreak - 1)
|
512
|
+
_set(msig, self._defns[sig])
|
513
|
+
self._defns[sig] = fn
|
514
|
+
|
515
|
+
_set(sig, fn)
|
507
516
|
|
508
517
|
self._update()
|
509
518
|
return self
|
@@ -373,6 +373,7 @@ class NameConverter(ast.NodeTransformer):
|
|
373
373
|
self.call_next_sym = call_next_sym
|
374
374
|
self.ovld_mangled = ovld_mangled
|
375
375
|
self.code_mangled = code_mangled
|
376
|
+
self.count = count()
|
376
377
|
|
377
378
|
def visit_Name(self, node):
|
378
379
|
if node.id == self.recurse_sym:
|
@@ -396,15 +397,16 @@ class NameConverter(ast.NodeTransformer):
|
|
396
397
|
return self.generic_visit(node)
|
397
398
|
|
398
399
|
cn = node.func.id == self.call_next_sym
|
400
|
+
tmp = f"__TMP{next(self.count)}_"
|
399
401
|
|
400
402
|
def _make_lookup_call(key, arg):
|
401
403
|
value = ast.NamedExpr(
|
402
|
-
target=ast.Name(id=f"
|
404
|
+
target=ast.Name(id=f"{tmp}{key}", ctx=ast.Store()),
|
403
405
|
value=self.visit(arg),
|
404
406
|
)
|
405
407
|
if self.analysis.lookup_for(key) == "self.map.transform":
|
406
408
|
func = ast.Attribute(
|
407
|
-
value=ast.Name(id="
|
409
|
+
value=ast.Name(id=f"{tmp}M", ctx=ast.Load()),
|
408
410
|
attr="transform",
|
409
411
|
ctx=ast.Load(),
|
410
412
|
)
|
@@ -437,7 +439,7 @@ class NameConverter(ast.NodeTransformer):
|
|
437
439
|
type_parts.insert(0, ast.Name(id=self.code_mangled, ctx=ast.Load()))
|
438
440
|
method = ast.Subscript(
|
439
441
|
value=ast.NamedExpr(
|
440
|
-
target=ast.Name(id="
|
442
|
+
target=ast.Name(id=f"{tmp}M", ctx=ast.Store()),
|
441
443
|
value=ast.Attribute(
|
442
444
|
value=ast.Name(id=self.ovld_mangled, ctx=ast.Load()),
|
443
445
|
attr="map",
|
@@ -464,13 +466,13 @@ class NameConverter(ast.NodeTransformer):
|
|
464
466
|
new_node = ast.Call(
|
465
467
|
func=method,
|
466
468
|
args=[
|
467
|
-
ast.Name(id=f"
|
469
|
+
ast.Name(id=f"{tmp}{i}", ctx=ast.Load())
|
468
470
|
for i, arg in enumerate(node.args)
|
469
471
|
],
|
470
472
|
keywords=[
|
471
473
|
ast.keyword(
|
472
474
|
arg=kw.arg,
|
473
|
-
value=ast.Name(id=f"
|
475
|
+
value=ast.Name(id=f"{tmp}{kw.arg}", ctx=ast.Load()),
|
474
476
|
)
|
475
477
|
for kw in node.keywords
|
476
478
|
],
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import inspect
|
2
2
|
import math
|
3
3
|
import typing
|
4
|
+
from dataclasses import dataclass
|
4
5
|
from itertools import count
|
5
6
|
from types import CodeType
|
6
7
|
|
@@ -69,6 +70,27 @@ class TypeMap(dict):
|
|
69
70
|
raise KeyError(obj_t)
|
70
71
|
|
71
72
|
|
73
|
+
@dataclass
|
74
|
+
class Candidate:
|
75
|
+
handler: object
|
76
|
+
priority: float
|
77
|
+
specificity: tuple
|
78
|
+
tiebreak: int
|
79
|
+
|
80
|
+
def sort_key(self):
|
81
|
+
return self.priority, sum(self.specificity), self.tiebreak
|
82
|
+
|
83
|
+
def dominates(self, other):
|
84
|
+
if self.priority > other.priority:
|
85
|
+
return True
|
86
|
+
elif self.specificity != other.specificity:
|
87
|
+
return all(
|
88
|
+
s1 >= s2 for s1, s2 in zip(self.specificity, other.specificity)
|
89
|
+
)
|
90
|
+
else:
|
91
|
+
return self.tiebreak > other.tiebreak
|
92
|
+
|
93
|
+
|
72
94
|
class MultiTypeMap(dict):
|
73
95
|
"""Represents a mapping from tuples of types to handlers.
|
74
96
|
|
@@ -91,6 +113,7 @@ class MultiTypeMap(dict):
|
|
91
113
|
def __init__(self, name="_ovld", key_error=KeyError):
|
92
114
|
self.maps = {}
|
93
115
|
self.priorities = {}
|
116
|
+
self.tiebreaks = {}
|
94
117
|
self.dependent = {}
|
95
118
|
self.type_tuples = {}
|
96
119
|
self.empty = MISSING
|
@@ -155,7 +178,12 @@ class MultiTypeMap(dict):
|
|
155
178
|
specificities.setdefault(c, []).append(results[c])
|
156
179
|
|
157
180
|
candidates = [
|
158
|
-
(
|
181
|
+
Candidate(
|
182
|
+
handler=c,
|
183
|
+
priority=self.priorities.get(c, 0),
|
184
|
+
specificity=tuple(specificities[c]),
|
185
|
+
tiebreak=self.tiebreaks.get(c, 0),
|
186
|
+
)
|
159
187
|
for c in candidates
|
160
188
|
]
|
161
189
|
|
@@ -164,33 +192,30 @@ class MultiTypeMap(dict):
|
|
164
192
|
# other possibilities on all arguments, so the sum of all specificities
|
165
193
|
# has to be greater.
|
166
194
|
# Note: priority is always more important than specificity
|
167
|
-
|
195
|
+
|
196
|
+
candidates.sort(key=Candidate.sort_key, reverse=True)
|
168
197
|
|
169
198
|
self.all[obj_t_tup] = {
|
170
|
-
getattr(c
|
199
|
+
getattr(c.handler, "__code__", None) for c in candidates
|
171
200
|
}
|
172
201
|
|
173
202
|
processed = set()
|
174
203
|
|
175
204
|
def _pull(candidates):
|
176
|
-
candidates = [
|
177
|
-
(c, a, b) for (c, a, b) in candidates if c not in processed
|
178
|
-
]
|
205
|
+
candidates = [c for c in candidates if c.handler not in processed]
|
179
206
|
if not candidates:
|
180
207
|
return
|
181
208
|
rval = [candidates[0]]
|
182
|
-
c1
|
183
|
-
for c2
|
184
|
-
if
|
185
|
-
spc1 != spc2 and all(s1 >= s2 for s1, s2 in zip(spc1, spc2))
|
186
|
-
):
|
209
|
+
c1 = candidates[0]
|
210
|
+
for c2 in candidates[1:]:
|
211
|
+
if c1.dominates(c2):
|
187
212
|
# Candidate 1 dominates candidate 2
|
188
213
|
continue
|
189
214
|
else:
|
190
|
-
processed.add(c2)
|
215
|
+
processed.add(c2.handler)
|
191
216
|
# Candidate 1 does not dominate candidate 2, so we add it
|
192
217
|
# to the list.
|
193
|
-
rval.append(
|
218
|
+
rval.append(c2)
|
194
219
|
yield rval
|
195
220
|
if len(rval) >= 1:
|
196
221
|
yield from _pull(candidates[1:])
|
@@ -212,6 +237,7 @@ class MultiTypeMap(dict):
|
|
212
237
|
self.empty = entry
|
213
238
|
|
214
239
|
self.priorities[handler] = sig.priority
|
240
|
+
self.tiebreaks[handler] = sig.tiebreak
|
215
241
|
self.type_tuples[handler] = obj_t_tup
|
216
242
|
self.dependent[handler] = any(
|
217
243
|
isinstance(t[1] if isinstance(t, tuple) else t, DependentType)
|
@@ -257,15 +283,16 @@ class MultiTypeMap(dict):
|
|
257
283
|
finished = False
|
258
284
|
rank = 1
|
259
285
|
for grp in self.mro(tuple(argt)):
|
260
|
-
grp.sort(key=lambda x: x
|
286
|
+
grp.sort(key=lambda x: x.handler.__name__)
|
261
287
|
match = [
|
262
288
|
dependent_match(
|
263
|
-
self.type_tuples[handler], [*args, *kwargs.items()]
|
289
|
+
self.type_tuples[c.handler], [*args, *kwargs.items()]
|
264
290
|
)
|
265
|
-
for
|
291
|
+
for c in grp
|
266
292
|
]
|
267
293
|
ambiguous = len([m for m in match if m]) > 1
|
268
|
-
for m,
|
294
|
+
for m, c in zip(match, grp):
|
295
|
+
handler = c.handler
|
269
296
|
color = "\033[0m"
|
270
297
|
if finished:
|
271
298
|
bullet = "--"
|
@@ -282,8 +309,8 @@ class MultiTypeMap(dict):
|
|
282
309
|
message = f"{handler.__name__} will be called first."
|
283
310
|
color = "\033[1;32m"
|
284
311
|
rank += 1
|
285
|
-
spec = ".".join(map(str,
|
286
|
-
lvl = f"[{
|
312
|
+
spec = ".".join(map(str, c.specificity))
|
313
|
+
lvl = f"[{c.priority}:{spec}]"
|
287
314
|
width = 2 * len(args) + 6
|
288
315
|
print(f"{color}{bullet} {lvl:{width}} {handler.__name__}")
|
289
316
|
co = handler.__code__
|
@@ -320,8 +347,8 @@ class MultiTypeMap(dict):
|
|
320
347
|
|
321
348
|
funcs = []
|
322
349
|
for group in reversed(results):
|
323
|
-
handlers = [
|
324
|
-
dependent = any(self.dependent[
|
350
|
+
handlers = [c.handler for c in group]
|
351
|
+
dependent = any(self.dependent[c.handler] for c in group)
|
325
352
|
if dependent:
|
326
353
|
nxt = self.wrap_dependent(
|
327
354
|
obj_t_tup, handlers, group, funcs[-1] if funcs else None
|
@@ -0,0 +1 @@
|
|
1
|
+
version = "0.4.2"
|
@@ -559,6 +559,22 @@ def test_recurse_method():
|
|
559
559
|
assert C().f([1, 2, 3]) == [2, 3, 4]
|
560
560
|
|
561
561
|
|
562
|
+
def test_recurse_nested():
|
563
|
+
@ovld
|
564
|
+
def f(xs: list):
|
565
|
+
return recurse(sum(recurse(x) for x in xs))
|
566
|
+
|
567
|
+
@ovld
|
568
|
+
def f(x: str):
|
569
|
+
return len(x)
|
570
|
+
|
571
|
+
@ovld
|
572
|
+
def f(x: int):
|
573
|
+
return x * x
|
574
|
+
|
575
|
+
assert f(["a", "bbb", "cc"]) == 36
|
576
|
+
|
577
|
+
|
562
578
|
def test_call_next():
|
563
579
|
f = Ovld()
|
564
580
|
|
@@ -573,6 +589,44 @@ def test_call_next():
|
|
573
589
|
assert f(3) == 8
|
574
590
|
|
575
591
|
|
592
|
+
def test_call_next_unrelated():
|
593
|
+
f = Ovld()
|
594
|
+
|
595
|
+
@f.register
|
596
|
+
def f(x: int):
|
597
|
+
return f.next(str(x))
|
598
|
+
|
599
|
+
@f.register
|
600
|
+
def f(x: str):
|
601
|
+
return x * 2
|
602
|
+
|
603
|
+
@f.register
|
604
|
+
def f(x: object):
|
605
|
+
return "no"
|
606
|
+
|
607
|
+
assert f(3) == "33"
|
608
|
+
for k, v in f.map.items():
|
609
|
+
print(k, v)
|
610
|
+
|
611
|
+
|
612
|
+
def test_call_next_same_priority():
|
613
|
+
f = Ovld()
|
614
|
+
|
615
|
+
@f.register
|
616
|
+
def f(x: int):
|
617
|
+
return x * 2
|
618
|
+
|
619
|
+
@f.register
|
620
|
+
def f(x: int):
|
621
|
+
return call_next(x + 1)
|
622
|
+
|
623
|
+
@f.register
|
624
|
+
def f(x: int):
|
625
|
+
return call_next(-x)
|
626
|
+
|
627
|
+
assert f(5) == -8
|
628
|
+
|
629
|
+
|
576
630
|
def test_recurse_renamed():
|
577
631
|
f = Ovld()
|
578
632
|
|
@@ -1540,11 +1594,16 @@ def test_keywords():
|
|
1540
1594
|
def f(name: str, *, hello: int):
|
1541
1595
|
return "hello" * hello + " " + name
|
1542
1596
|
|
1597
|
+
@ovld
|
1598
|
+
def f(name: str, *, hello: str):
|
1599
|
+
return hello + " " + name
|
1600
|
+
|
1543
1601
|
@f.register
|
1544
1602
|
def f(name: str, *, goodbye: int):
|
1545
1603
|
return "goodbye" * goodbye + " " + name
|
1546
1604
|
|
1547
1605
|
assert f("Helena", hello=3) == "hellohellohello Helena"
|
1606
|
+
assert f("Helena", hello="Bonjour") == "Bonjour Helena"
|
1548
1607
|
assert f("Gertrude", goodbye=2) == "goodbyegoodbye Gertrude"
|
1549
1608
|
|
1550
1609
|
|
{ovld-0.4.1 → ovld-0.4.2}/todo.q
RENAMED
ovld-0.4.1/src/ovld/version.py
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
version = "0.4.1"
|
File without changes
|
{ovld-0.4.1 → ovld-0.4.2}/.envrc
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ovld-0.4.1 → ovld-0.4.2}/add.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ovld-0.4.1 → ovld-0.4.2}/doo.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ovld-0.4.1 → ovld-0.4.2}/nxt.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ovld-0.4.1 → ovld-0.4.2}/tb.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ovld-0.4.1 → ovld-0.4.2}/x.py
RENAMED
File without changes
|
{ovld-0.4.1 → ovld-0.4.2}/zaggo
RENAMED
File without changes
|