ovld 0.3.9__py3-none-any.whl → 0.4.0__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/utils.py CHANGED
@@ -1,9 +1,6 @@
1
1
  """Miscellaneous utilities."""
2
2
 
3
3
  import functools
4
- import sys
5
- from dataclasses import dataclass
6
- from typing import Protocol, runtime_checkable
7
4
 
8
5
 
9
6
  class Named:
@@ -48,122 +45,24 @@ def keyword_decorator(deco):
48
45
  return new_deco
49
46
 
50
47
 
51
- class _MetaMC(type):
52
- def __subclasscheck__(cls, sub):
53
- return cls.chk(sub)
48
+ class UsageError(Exception):
49
+ pass
54
50
 
55
51
 
56
- def meta(condition):
57
- """Return a class with a subclassing relation defined by condition.
52
+ class Unusable:
53
+ def __init__(self, message):
54
+ self.__message = message
58
55
 
59
- For example, a dataclass is a subclass of `meta(dataclasses.is_dataclass)`,
60
- and a class which name starts with "X" is a subclass of
61
- `meta(lambda cls: cls.__name__.startswith("X"))`.
56
+ def __call__(self, *args, **kwargs):
57
+ raise UsageError(self.__message)
62
58
 
63
- Arguments:
64
- condition: A function that takes a class as an argument and returns
65
- True or False depending on whether it matches some condition.
66
- """
67
-
68
- class M(metaclass=_MetaMC):
69
- @classmethod
70
- def chk(cls, sub):
71
- return condition(sub)
72
-
73
- return M
74
-
75
-
76
- def _getcls(ref):
77
- module, *parts = ref.split(".")
78
- curr = __import__(module)
79
- for part in parts:
80
- curr = getattr(curr, part)
81
- return curr
82
-
83
-
84
- def deferred(ref):
85
- """Represent a class from an external module without importing it.
86
-
87
- For instance, `deferred("numpy.ndarray")` matches instances of
88
- numpy.ndarray, but it does not import numpy. When tested against a
89
- class, if the first part of class's `__module__` is `numpy`, then
90
- we do get the class and perform a normal issubclass check.
91
-
92
- If the module is already loaded, `deferred` returns the class directly.
93
-
94
- Arguments:
95
- ref: A string starting with a module name representing the path
96
- to import a class.
97
- """
98
- module, _ = ref.split(".", 1)
99
- if module in sys.modules:
100
- return _getcls(ref)
101
-
102
- @meta
103
- def check(cls):
104
- full_cls_mod = getattr(cls, "__module__", None)
105
- cls_module = full_cls_mod.split(".", 1)[0] if full_cls_mod else None
106
- if cls_module == module:
107
- return issubclass(cls, _getcls(ref))
108
- else:
109
- return False
110
-
111
- return check
112
-
113
-
114
- def exactly(base_cls):
115
- """Match the class but not its subclasses."""
116
-
117
- @meta
118
- def check(cls):
119
- return cls is base_cls
120
-
121
- return check
122
-
123
-
124
- def strict_subclass(base_cls):
125
- """Match subclasses but not the base class."""
126
-
127
- @meta
128
- def check(cls):
129
- return (
130
- isinstance(cls, type)
131
- and issubclass(cls, base_cls)
132
- and cls is not base_cls
133
- )
134
-
135
- return check
136
-
137
-
138
- def has_attribute(*attrs):
139
- """Match classes with the given attributes."""
140
-
141
- @meta
142
- def check(cls):
143
- return all(hasattr(cls, a) for a in attrs)
144
-
145
- return check
146
-
147
-
148
- @runtime_checkable
149
- @dataclass
150
- class Dataclass(Protocol):
151
- @classmethod
152
- def __subclasshook__(cls, subclass):
153
- return hasattr(subclass, "__dataclass_fields__") and hasattr(
154
- subclass, "__dataclass_params__"
155
- )
59
+ def __getattr__(self, attr):
60
+ raise UsageError(self.__message)
156
61
 
157
62
 
158
63
  __all__ = [
159
64
  "BOOTSTRAP",
160
65
  "MISSING",
161
- "Dataclass",
162
66
  "Named",
163
- "deferred",
164
- "exactly",
165
- "has_attribute",
166
- "meta",
167
67
  "keyword_decorator",
168
- "strict_subclass",
169
68
  ]
ovld/version.py CHANGED
@@ -1 +1 @@
1
- version = "0.3.9"
1
+ version = "0.4.0"
@@ -0,0 +1,213 @@
1
+ Metadata-Version: 2.3
2
+ Name: ovld
3
+ Version: 0.4.0
4
+ Summary: Overloading Python functions
5
+ Author-email: Olivier Breuleux <breuleux@gmail.com>
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Requires-Python: >=3.9
9
+ Description-Content-Type: text/markdown
10
+
11
+
12
+ # Ovld
13
+
14
+ Fast multiple dispatch in Python, with many extra features.
15
+
16
+ [📋 Documentation](https://ovld.readthedocs.io/en/latest/)
17
+
18
+ With ovld, you can write a version of the same function for every type signature using annotations instead of writing an awkward sequence of `isinstance` statements. Unlike Python's `singledispatch`, it works for multiple arguments.
19
+
20
+ * ⚡️ **[Fast](https://ovld.readthedocs.io/en/latest/compare/#results):** `ovld` is the fastest multiple dispatch library around, by some margin.
21
+ * 🚀 [**Variants**](https://ovld.readthedocs.io/en/latest/usage/#variants) and [**mixins**](https://ovld.readthedocs.io/en/latest/usage/#mixins) of functions and methods.
22
+ * 🦄 **[Dependent types](https://ovld.readthedocs.io/en/latest/dependent/):** Overloaded functions can depend on more than argument types: they can depend on actual values.
23
+ * 🔑 **Extensive:** Dispatch on functions, methods, positional arguments and even [keyword arguments](https://ovld.readthedocs.io/en/latest/usage/#keyword-arguments) (with some restrictions).
24
+
25
+ ## Example
26
+
27
+ Here's a function that recursively adds lists, tuples and dictionaries:
28
+
29
+ ```python
30
+ from ovld import ovld, recurse
31
+
32
+ @ovld
33
+ def add(x: list, y: list):
34
+ return [recurse(a, b) for a, b in zip(x, y)]
35
+
36
+ @ovld
37
+ def add(x: tuple, y: tuple):
38
+ return tuple(recurse(a, b) for a, b in zip(x, y))
39
+
40
+ @ovld
41
+ def add(x: dict, y: dict):
42
+ return {k: recurse(v, y[k]) for k, v in x.items()}
43
+
44
+ @ovld
45
+ def add(x: object, y: object):
46
+ return x + y
47
+
48
+ assert add([1, 2], [3, 4]) == [4, 6]
49
+ ```
50
+
51
+ The `recurse` function is special: it will recursively call the current ovld object. You may ask: how is it different from simply calling `add`? The difference is that if you create a *variant* of `add`, `recurse` will automatically call the variant.
52
+
53
+ For example:
54
+
55
+
56
+ ## Variants
57
+
58
+ A *variant* of an `ovld` is a copy of the `ovld`, with some methods added or changed. For example, let's take the definition of `add` above and make a variant that multiplies numbers instead:
59
+
60
+ ```python
61
+ @add.variant
62
+ def mul(self, x: object, y: object):
63
+ return x * y
64
+
65
+ assert mul([1, 2], [3, 4]) == [3, 8]
66
+ ```
67
+
68
+ Simple! This means you can define one `ovld` that recursively walks generic data structures, and then specialize it in various ways.
69
+
70
+
71
+ ## Priority and call_next
72
+
73
+ You can define a numeric priority for each method (the default priority is 0):
74
+
75
+ ```python
76
+ from ovld import call_next
77
+
78
+ @ovld(priority=1000)
79
+ def f(x: int):
80
+ return call_next(x + 1)
81
+
82
+ @ovld
83
+ def f(x: int):
84
+ return x * x
85
+
86
+ assert f(10) == 121
87
+ ```
88
+
89
+ Both definitions above have the same type signature, but since the first has higher priority, that is the one that will be called.
90
+
91
+ However, that does not mean there is no way to call the second one. Indeed, when the first function calls the special function `call_next(x + 1)`, it will call the next function in the list below itself.
92
+
93
+ The pattern you see above is how you may wrap each call with some generic behavior. For instance, if you did something like that:
94
+
95
+ ```python
96
+ @f.variant(priority=1000)
97
+ def f2(x: object)
98
+ print(f"f({x!r})")
99
+ return call_next(x)
100
+ ```
101
+
102
+ You would effectively be creating a clone of `f` that traces every call.
103
+
104
+
105
+ ## Dependent types
106
+
107
+ A dependent type is a type that depends on a value. `ovld` supports this, either through `Literal[value]` or `Dependent[bound, check]`. For example, this definition of factorial:
108
+
109
+ ```python
110
+ from typing import Literal
111
+ from ovld import ovld, recurse, Dependent
112
+
113
+ @ovld
114
+ def fact(n: Literal[0]):
115
+ return 1
116
+
117
+ @ovld
118
+ def fact(n: Dependent[int, lambda n: n > 0]):
119
+ return n * recurse(n - 1)
120
+
121
+ assert fact(5) == 120
122
+ fact(-1) # Error!
123
+ ```
124
+
125
+ The first argument to `Dependent` must be a type bound. The bound must match before the logic is called, which also ensures we don't get a performance hit for unrelated types. For type checking purposes, `Dependent[T, A]` is equivalent to `Annotated[T, A]`.
126
+
127
+ ### dependent_check
128
+
129
+ Define your own types with the `@dependent_check` decorator:
130
+
131
+ ```python
132
+ import torch
133
+ from ovld import ovld, dependent_check
134
+
135
+ @dependent_check
136
+ def Shape(tensor: torch.Tensor, *shape):
137
+ return (
138
+ len(tensor.shape) == len(shape)
139
+ and all(s2 is Any or s1 == s2 for s1, s2 in zip(tensor.shape, shape))
140
+ )
141
+
142
+ @dependent_check
143
+ def Dtype(tensor: torch.Tensor, dtype):
144
+ return tensor.dtype == dtype
145
+
146
+ @ovld
147
+ def f(tensor: Shape[3, Any]):
148
+ # Matches 3xN tensors
149
+ ...
150
+
151
+ @ovld
152
+ def f(tensor: Shape[2, 2] & Dtype[torch.float32]):
153
+ # Only matches 2x2 tensors that also have the float32 dtype
154
+ ...
155
+ ```
156
+
157
+ The first parameter is the value to check. The type annotation (e.g. `value: torch.Tensor` above) is interpreted by `ovld` to be the bound for this type, so `Shape` will only be called on parameters of type `torch.Tensor`.
158
+
159
+ ## Methods
160
+
161
+ Either inherit from `OvldBase` or use the `OvldMC` metaclass to use multiple dispatch on methods.
162
+
163
+ ```python
164
+ from ovld import OvldBase, OvldMC
165
+
166
+ # class Cat(OvldBase): <= Also an option
167
+ class Cat(metaclass=OvldMC):
168
+ def interact(self, x: Mouse):
169
+ return "catch"
170
+
171
+ def interact(self, x: Food):
172
+ return "devour"
173
+
174
+ def interact(self, x: PricelessVase):
175
+ return "destroy"
176
+ ```
177
+
178
+ ### Subclasses
179
+
180
+ Subclasses inherit overloaded methods. They may define additional overloads for these methods which will only be valid for the subclass, but they need to use the `@extend_super` decorator (this is required for clarity):
181
+
182
+
183
+ ```python
184
+ from ovld import OvldMC, extend_super
185
+
186
+ class One(metaclass=OvldMC):
187
+ def f(self, x: int):
188
+ return "an integer"
189
+
190
+ class Two(One):
191
+ @extend_super
192
+ def f(self, x: str):
193
+ return "a string"
194
+
195
+ assert Two().f(1) == "an integer"
196
+ assert Two().f("s") == "a string"
197
+ ```
198
+
199
+ # Benchmarks
200
+
201
+ `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.
202
+
203
+ Time relative to the fastest implementation (1.00) (lower is better).
204
+
205
+ | 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) |
206
+ | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
207
+ |[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|
208
+ |[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|
209
+ |[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|
210
+ |[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|
211
+ |[calc](https://github.com/breuleux/ovld/tree/master/benchmarks/test_calc.py)|1.00|1.96|80.00|43.21|x|x|x|x|
212
+ |[fib](https://github.com/breuleux/ovld/tree/master/benchmarks/test_fib.py)|1.00|3.58|438.97|123.58|x|x|x|x|
213
+ |[tweak](https://github.com/breuleux/ovld/tree/master/benchmarks/test_tweaknum.py)|1.00|2.59|x|x|x|x|x|x|
@@ -0,0 +1,13 @@
1
+ ovld/__init__.py,sha256=nF8ymgr8dSL2WvDpOXVxqDuEGUywbkiCGr4ICumMD8U,1286
2
+ ovld/core.py,sha256=siahCDuw78n02-2g7TGI1GerrGvPxT0zPouN6Z30Z-g,25497
3
+ ovld/dependent.py,sha256=QITsWu2uCwqkHE0tunETy8Jqwc272uoG5YM0I5yy0m4,7303
4
+ ovld/mro.py,sha256=dNuVW5DIOT8TJtY4vEpxT4q3R8fQxJ8UyDQSD9oFYYo,5116
5
+ ovld/recode.py,sha256=Vc97Nv1j2GWuitLOzIuIhpscZ4gaOJQP3hLNp8SGTQ8,17890
6
+ ovld/typemap.py,sha256=U_BmXtts1oYVa6gI3cgGHMX5kFcCJY_mt_cjWvDo3jQ,12979
7
+ ovld/types.py,sha256=Zeb7xhHbL4T7qIRHI-I_cjG61UIWrfZv_EwjwqhB-rY,6381
8
+ ovld/utils.py,sha256=V6Y8oZ6ojq8JaODL1rMZbU5L9QG0YSqHNYmpIFiwy3M,1294
9
+ ovld/version.py,sha256=yhiWOz0HoJGRRI9-JQ2eh_0AbByy-6psK08-kpTSHJw,18
10
+ ovld-0.4.0.dist-info/METADATA,sha256=4NkzmXbqa8ZNCwESYT4U8yILZyaIlw1rcghfCyaT50U,7526
11
+ ovld-0.4.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
12
+ ovld-0.4.0.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
13
+ ovld-0.4.0.dist-info/RECORD,,
@@ -1,305 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: ovld
3
- Version: 0.3.9
4
- Summary: Overloading Python functions
5
- Author-email: Olivier Breuleux <breuleux@gmail.com>
6
- License-Expression: MIT
7
- License-File: LICENSE
8
- Requires-Python: >=3.8
9
- Description-Content-Type: text/markdown
10
-
11
-
12
- # Ovld
13
-
14
- Multiple dispatch in Python, with some extra features.
15
-
16
- With ovld, you can write a version of the same function for every type signature using annotations instead of writing an awkward sequence of `isinstance` statements. Unlike Python `singledispatch`, it works for multiple arguments.
17
-
18
- Other features of `ovld`:
19
-
20
- * Multiple dispatch for methods (with `metaclass=ovld.OvldMC`)
21
- * Create variants of functions
22
- * Built-in support for extensible recursion
23
- * Function wrappers
24
- * Function postprocessors
25
- * Nice stack traces
26
-
27
- ## Example
28
-
29
- Here's a function that adds lists, tuples and dictionaries:
30
-
31
- ```python
32
- from ovld import ovld
33
-
34
- @ovld
35
- def add(x: list, y: list):
36
- return [add(a, b) for a, b in zip(x, y)]
37
-
38
- @ovld
39
- def add(x: tuple, y: tuple):
40
- return tuple(add(a, b) for a, b in zip(x, y))
41
-
42
- @ovld
43
- def add(x: dict, y: dict):
44
- return {k: add(v, y[k]) for k, v in x.items()}
45
-
46
- @ovld
47
- def add(x: object, y: object):
48
- return x + y
49
- ```
50
-
51
- ## Bootstrapping and variants
52
-
53
- Now, there is another way to do this using ovld's *auto-bootstrapping*. Simply list `self` as the first argument to the function, and `self` will be bound to the function itself, so you can call `self(x, y)` for the recursion instead of `add(x, y)`:
54
-
55
-
56
- ```python
57
- @ovld
58
- def add(self, x: list, y: list):
59
- return [self(a, b) for a, b in zip(x, y)]
60
-
61
- @ovld
62
- def add(self, x: tuple, y: tuple):
63
- return tuple(self(a, b) for a, b in zip(x, y))
64
-
65
- @ovld
66
- def add(self, x: dict, y: dict):
67
- return {k: self(v, y[k]) for k, v in x.items()}
68
-
69
- @ovld
70
- def add(self, x: object, y: object):
71
- return x + y
72
- ```
73
-
74
- Why is this useful, though? Observe:
75
-
76
- ```python
77
- @add.variant
78
- def mul(self, x: object, y: object):
79
- return x * y
80
-
81
- assert add([1, 2], [3, 4]) == [4, 6]
82
- assert mul([1, 2], [3, 4]) == [3, 8]
83
- ```
84
-
85
- A `variant` of a function is a copy which inherits all of the original's implementations but may define new ones. And because `self` is bound to the function that's called at the top level, the implementations for `list`, `tuple` and `dict` will bind `self` to `add` or `mul` depending on which one was called. You may also call `self.super(*args)` to invoke the parent implementation for that type.
86
-
87
-
88
- ## Custom dispatch
89
-
90
- You can define your own dispatching function. The dispatcher's first argument is always `self`.
91
-
92
- * `self.resolve(x, y)` to get the right function for the types of x and y
93
- * `self[type(x), type(y)]` will also return the right function for these types, but it works directly with the types.
94
-
95
- For example, here is how you might define a function such that f(x) <=> f(x, x):
96
-
97
- ```python
98
- @ovld.dispatch
99
- def add_default(self, x, y=None):
100
- if y is None:
101
- y = x
102
- return self.resolve(x, y)(x, y)
103
-
104
- @ovld
105
- def add_default(x: int, y: int):
106
- return x + y
107
-
108
- @ovld
109
- def add_default(x: str, y: str):
110
- return x + y
111
-
112
- @ovld
113
- def add_default(xs: list, ys: list):
114
- return [add_default(x, y) for x, y in zip(xs, ys)]
115
-
116
- assert add_default([1, 2, "alouette"]) == [2, 4, "alouettealouette"]
117
- ```
118
-
119
- There are other uses for this feature, e.g. memoization.
120
-
121
- The normal functions may also have a `self`, which works the same as bootstrapping.
122
-
123
- ## Postprocess
124
-
125
- `@ovld`, `@ovld.dispatch`, etc. take a `postprocess` argument which should be a function of one argument. That function will be called with the result of the call and must return the final result of the call.
126
-
127
- Note that intermediate, bootstrapped recursive calls (recursive calls using `self()`) will **not** be postprocessed (if you want to wrap these calls, you can do so otherwise, like defining a custom dispatch). Only the result of the top level call is postprocessed.
128
-
129
- ## Methods
130
-
131
- Use the `OvldMC` metaclass to use multiple dispatch on methods. In this case there is no bootstrapping as described above and `self` is simply bound to the class instance.
132
-
133
- ```python
134
- from ovld import OvldMC
135
-
136
- class Cat(metaclass=OvldMC):
137
- def interact(self, x: Mouse):
138
- return "catch"
139
-
140
- def interact(self, x: Food):
141
- return "devour"
142
-
143
- def interact(self, x: PricelessVase):
144
- return "destroy"
145
- ```
146
-
147
- Subclasses of `Cat` will inherit the overloaded `interact` and it may define additional overloaded methods which will only be valid for the subclass.
148
-
149
- **Note:** It is possible to use `ovld.dispatch` on methods, but in this case be aware that the first argument for the dispatch method will not be the usual `self` but an `OvldCall` object. The `self` can be retrived as `ovldcall.obj`. Here's an example to make it all clear:
150
-
151
- ```python
152
- class Stuff(metaclass=OvldMC):
153
- def __init__(self, mul):
154
- self.mul = mul
155
-
156
- @ovld.dispatch
157
- def calc(ovldcall, x):
158
- # Wraps every call to self.calc, but we receive ovldcall instead of self
159
- # ovldcall[type(x)] returns the right method to call
160
- # ovldcall.obj is the self (the actual instance of Stuff)
161
- return ovldcall[type(x)](x) * ovldcall.obj.mul
162
-
163
- def calc(self, x: int):
164
- return x + 1
165
-
166
- def calc(self, xs: list):
167
- return [self.calc(x) for x in xs]
168
-
169
- print(Stuff(2).calc([1, 2, 3])) # [4, 6, 8, 4, 6, 8]
170
- ```
171
-
172
- ### Mixins in subclasses
173
-
174
- The `@extend_super` decorator on a method will combine the method with the definition on the superclass:
175
-
176
- ```python
177
- from ovld import OvldMC, extend_super
178
-
179
- class One(metaclass=OvldMC):
180
- def f(self, x: int):
181
- return "an integer"
182
-
183
- class Two(One):
184
- @extend_super
185
- def f(self, x: str):
186
- return "a string"
187
-
188
- assert Two().f(1) == "an integer"
189
- assert Two().f("s") == "a string"
190
- ```
191
-
192
- ## Ambiguous calls
193
-
194
- The following definitions will cause a TypeError at runtime when called with two ints, because it is unclear which function is the right match:
195
-
196
- ```python
197
- @ovld
198
- def ambig(x: int, y: object):
199
- print("io")
200
-
201
- @ovld
202
- def ambig(x: object, y: int):
203
- print("oi")
204
-
205
- ambig(8, 8) # ???
206
- ```
207
-
208
- You may define an additional function with signature (int, int) to disambiguate:
209
-
210
- ```python
211
- @ovld
212
- def ambig(x: int, y: int):
213
- print("ii")
214
- ```
215
-
216
- ## Other features
217
-
218
- ### meta
219
-
220
- To test arbitrary conditions, you can use `meta`:
221
-
222
- ```python
223
- from ovld import ovld, meta
224
-
225
- @meta
226
- def StartsWithT(cls):
227
- return cls.__name__.startswith("T")
228
-
229
- @ovld
230
- def f(x: StartsWithT):
231
- return "T"
232
-
233
- assert f(TypeError("xyz")) == "T"
234
-
235
-
236
- # Or: a useful example, since dataclasses have no common superclass:
237
-
238
- from dataclasses import dataclass, is_dataclass
239
-
240
- @dataclass
241
- class Point:
242
- x: int
243
- y: int
244
-
245
- @ovld
246
- def f(x: meta(is_dataclass)):
247
- return "dataclass"
248
-
249
- assert f(Point(1, 2)) == "dataclass"
250
- ```
251
-
252
-
253
- ### deferred
254
-
255
- You may define overloads for certain classes from external packages without
256
- having to import them:
257
-
258
-
259
- ```python
260
- from ovld import ovld, deferred
261
-
262
- @ovld
263
- def f(x: deferred("numpy.ndarray")):
264
- return "ndarray"
265
-
266
- # numpy is not imported
267
- assert "numpy" not in sys.modules
268
-
269
- # But once we import it, the ovld works:
270
- import numpy
271
- assert f(numpy.arange(10)) == "ndarray"
272
- ```
273
-
274
-
275
- ### Tracebacks
276
-
277
- `ovld` automagically renames functions so that the stack trace is more informative:
278
-
279
- ```python
280
- @add.variant
281
- def bad(self, x: object, y: object):
282
- raise Exception("Bad.")
283
-
284
- bad([1], [2])
285
-
286
- """
287
- File "/Users/breuleuo/code/ovld/ovld/core.py", line 148, in bad.entry
288
- res = ovc(*args, **kwargs)
289
- File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
290
- return method(self.bind_to, *args, **kwargs)
291
- File "example.py", line 6, in bad[list, list]
292
- return [self(a, b) for a, b in zip(x, y)]
293
- File "example.py", line 6, in <listcomp>
294
- return [self(a, b) for a, b in zip(x, y)]
295
- File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
296
- return method(self.bind_to, *args, **kwargs)
297
- File "example.py", line 26, in bad[*, *]
298
- raise Exception("Bad.")
299
- Exception: Bad.
300
- """
301
- ```
302
-
303
- The functions on the stack have names like `bad.entry`, `bad.dispatch`, `bad[list, list]` and `bad[*, *]` (`*` stands for `object`), which lets you better understand what happened just from the stack trace.
304
-
305
- This also means profilers will be able to differentiate between these paths and between variants, even if they share code paths.
@@ -1,10 +0,0 @@
1
- ovld/__init__.py,sha256=jqq7r0RhfvkqmQa8Wc7wqX3Qh5NbwL_6Nfr1sp88Kh0,734
2
- ovld/core.py,sha256=3JmnfLF6puVtWkc01Hnxuss4gVdWzX9mrLVMfysQqJY,32895
3
- ovld/mro.py,sha256=W6T5IPec6YQIvv8oSwg-xGHTzZP1zOuTZuaRCegbl-I,6296
4
- ovld/recode.py,sha256=45VmTUmk2GmWRfTLANMHhyzVcgBit2RvwULzyWpOMhI,2234
5
- ovld/utils.py,sha256=8kINtHrWdC0bNVccxYIkHBaGFk_YFpjtnjAa9GyXNmA,3861
6
- ovld/version.py,sha256=8WhSz7JLKtk-44zKbF-sqJmexEzvpM-d0cPHqr6cE5k,18
7
- ovld-0.3.9.dist-info/METADATA,sha256=wbN2H6NflCIkt-OZJvzrVXWNIz8bH0FTow83Ir0C8NM,8282
8
- ovld-0.3.9.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
9
- ovld-0.3.9.dist-info/licenses/LICENSE,sha256=cSwNTIzd1cbI89xt3PeZZYJP2y3j8Zus4bXgo4svpX8,1066
10
- ovld-0.3.9.dist-info/RECORD,,
File without changes