summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--python-ovld.spec1005
-rw-r--r--sources1
3 files changed, 1007 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..722eba6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/ovld-0.3.2.tar.gz
diff --git a/python-ovld.spec b/python-ovld.spec
new file mode 100644
index 0000000..3e60e58
--- /dev/null
+++ b/python-ovld.spec
@@ -0,0 +1,1005 @@
+%global _empty_manifest_terminate_build 0
+Name: python-ovld
+Version: 0.3.2
+Release: 1
+Summary: Overloading Python functions
+License: MIT
+URL: https://github.com/breuleux/ovld
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/e0/9b/6a6e2f879cfb55380e1405091c70edb6705eca195c576b53af13127fe192/ovld-0.3.2.tar.gz
+BuildArch: noarch
+
+
+%description
+
+# Ovld
+
+Multiple dispatch in Python, with some extra features.
+
+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.
+
+Other features of `ovld`:
+
+* Multiple dispatch for methods (with `metaclass=ovld.OvldMC`)
+* Create variants of functions
+* Built-in support for extensible, stateful recursion
+* Function wrappers
+* Function postprocessors
+* Nice stack traces
+
+## Example
+
+Here's a function that adds lists, tuples and dictionaries:
+
+```python
+from ovld import ovld
+
+@ovld
+def add(x: list, y: list):
+ return [add(a, b) for a, b in zip(x, y)]
+
+@ovld
+def add(x: tuple, y: tuple):
+ return tuple(add(a, b) for a, b in zip(x, y))
+
+@ovld
+def add(x: dict, y: dict):
+ return {k: add(v, y[k]) for k, v in x.items()}
+
+@ovld
+def add(x: object, y: object):
+ return x + y
+```
+
+## Bootstrapping and variants
+
+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)`:
+
+
+```python
+@ovld
+def add(self, x: list, y: list):
+ return [self(a, b) for a, b in zip(x, y)]
+
+@ovld
+def add(self, x: tuple, y: tuple):
+ return tuple(self(a, b) for a, b in zip(x, y))
+
+@ovld
+def add(self, x: dict, y: dict):
+ return {k: self(v, y[k]) for k, v in x.items()}
+
+@ovld
+def add(self, x: object, y: object):
+ return x + y
+```
+
+Why is this useful, though? Observe:
+
+```python
+@add.variant
+def mul(self, x: object, y: object):
+ return x * y
+
+assert add([1, 2], [3, 4]) == [4, 6]
+assert mul([1, 2], [3, 4]) == [3, 8]
+```
+
+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.
+
+## State
+
+You can pass `initial_state` to `@ovld` or `variant`. The initial state must be a function that takes no arguments. Its return value will be available in `self.state`. The state is initialized at the top level call, but recursive calls to `self` will preserve it.
+
+In other words, you can do something like this:
+
+```python
+@add.variant(initial_state=lambda: 0)
+def count(self, x, y):
+ self.state += 1
+ return (f"#{self.state}", x + y)
+
+assert count([1, 2, 3], [4, 5, 6]) == [("#1", 5), ("#2", 7), ("#3", 9)]
+```
+
+The initial_state function can return any object and you can use the state to any purpose (e.g. cache or memoization).
+
+## Custom dispatch
+
+You can define your own dispatching function. The dispatcher's first argument is always `self`.
+
+* `self.resolve(x, y)` to get the right function for the types of x and y
+* `self[type(x), type(y)]` will also return the right function for these types, but it works directly with the types.
+
+For example, here is how you might define a function such that f(x) <=> f(x, x):
+
+```python
+@ovld.dispatch
+def add_default(self, x, y=None):
+ if y is None:
+ y = x
+ return self.resolve(x, y)(x, y)
+
+@ovld
+def add_default(x: int, y: int):
+ return x + y
+
+@ovld
+def add_default(x: str, y: str):
+ return x + y
+
+@ovld
+def add_default(xs: list, ys: list):
+ return [add_default(x, y) for x, y in zip(xs, ys)]
+
+assert add_default([1, 2, "alouette"]) == [2, 4, "alouettealouette"]
+```
+
+There are other uses for this feature, e.g. memoization.
+
+The normal functions may also have a `self`, which works the same as bootstrapping, and you can give an `initial_state` to `@ovld.dispatch` as well.
+
+## Postprocess
+
+`@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.
+
+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.
+
+## Methods
+
+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.
+
+```python
+from ovld import OvldMC
+
+class Cat(metaclass=OvldMC):
+ def interact(self, x: Mouse):
+ return "catch"
+
+ def interact(self, x: Food):
+ return "devour"
+
+ def interact(self, x: PricelessVase):
+ return "destroy"
+```
+
+Subclasses of `Cat` will inherit the overloaded `interact` and it may define additional overloaded methods which will only be valid for the subclass.
+
+**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:
+
+```python
+class Stuff(metaclass=OvldMC):
+ def __init__(self, mul):
+ self.mul = mul
+
+ @ovld.dispatch
+ def calc(ovldcall, x):
+ # Wraps every call to self.calc, but we receive ovldcall instead of self
+ # ovldcall[type(x)] returns the right method to call
+ # ovldcall.obj is the self (the actual instance of Stuff)
+ return ovldcall[type(x)](x) * ovldcall.obj.mul
+
+ def calc(self, x: int):
+ return x + 1
+
+ def calc(self, xs: list):
+ return [self.calc(x) for x in xs]
+
+print(Stuff(2).calc([1, 2, 3])) # [4, 6, 8, 4, 6, 8]
+```
+
+### Mixins in subclasses
+
+The `@extend_super` decorator on a method will combine the method with the definition on the superclass:
+
+```python
+from ovld import OvldMC, extend_super
+
+class One(metaclass=OvldMC):
+ def f(self, x: int):
+ return "an integer"
+
+class Two(One):
+ @extend_super
+ def f(self, x: str):
+ return "a string"
+
+assert Two().f(1) == "an integer"
+assert Two().f("s") == "a string"
+```
+
+## Ambiguous calls
+
+The following definitions will cause a TypeError at runtime when called with two ints, because it is unclear which function is the right match:
+
+```python
+@ovld
+def ambig(x: int, y: object):
+ print("io")
+
+@ovld
+def ambig(x: object, y: int):
+ print("oi")
+
+ambig(8, 8) # ???
+```
+
+You may define an additional function with signature (int, int) to disambiguate:
+
+```python
+@ovld
+def ambig(x: int, y: int):
+ print("ii")
+```
+
+## Other features
+
+### meta
+
+To test arbitrary conditions, you can use `meta`:
+
+```python
+from ovld import ovld, meta
+
+@meta
+def StartsWithT(cls):
+ return cls.__name__.startswith("T")
+
+@ovld
+def f(x: StartsWithT):
+ return "T"
+
+assert f(TypeError("xyz")) == "T"
+
+
+# Or: a useful example, since dataclasses have no common superclass:
+
+from dataclasses import dataclass, is_dataclass
+
+@dataclass
+class Point:
+ x: int
+ y: int
+
+@ovld
+def f(x: meta(is_dataclass)):
+ return "dataclass"
+
+assert f(Point(1, 2)) == "dataclass"
+```
+
+
+### deferred
+
+You may define overloads for certain classes from external packages without
+having to import them:
+
+
+```python
+from ovld import ovld, deferred
+
+@ovld
+def f(x: deferred("numpy.ndarray")):
+ return "ndarray"
+
+# numpy is not imported
+assert "numpy" not in sys.modules
+
+# But once we import it, the ovld works:
+import numpy
+assert f(numpy.arange(10)) == "ndarray"
+```
+
+
+### Tracebacks
+
+`ovld` automagically renames functions so that the stack trace is more informative:
+
+```python
+@add.variant
+def bad(self, x: object, y: object):
+ raise Exception("Bad.")
+
+bad([1], [2])
+
+"""
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 148, in bad.entry
+ res = ovc(*args, **kwargs)
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
+ return method(self.bind_to, *args, **kwargs)
+ File "example.py", line 6, in bad[list, list]
+ return [self(a, b) for a, b in zip(x, y)]
+ File "example.py", line 6, in <listcomp>
+ return [self(a, b) for a, b in zip(x, y)]
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
+ return method(self.bind_to, *args, **kwargs)
+ File "example.py", line 26, in bad[*, *]
+ raise Exception("Bad.")
+ Exception: Bad.
+"""
+```
+
+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.
+
+This also means profilers will be able to differentiate between these paths and between variants, even if they share code paths.
+
+
+%package -n python3-ovld
+Summary: Overloading Python functions
+Provides: python-ovld
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-ovld
+
+# Ovld
+
+Multiple dispatch in Python, with some extra features.
+
+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.
+
+Other features of `ovld`:
+
+* Multiple dispatch for methods (with `metaclass=ovld.OvldMC`)
+* Create variants of functions
+* Built-in support for extensible, stateful recursion
+* Function wrappers
+* Function postprocessors
+* Nice stack traces
+
+## Example
+
+Here's a function that adds lists, tuples and dictionaries:
+
+```python
+from ovld import ovld
+
+@ovld
+def add(x: list, y: list):
+ return [add(a, b) for a, b in zip(x, y)]
+
+@ovld
+def add(x: tuple, y: tuple):
+ return tuple(add(a, b) for a, b in zip(x, y))
+
+@ovld
+def add(x: dict, y: dict):
+ return {k: add(v, y[k]) for k, v in x.items()}
+
+@ovld
+def add(x: object, y: object):
+ return x + y
+```
+
+## Bootstrapping and variants
+
+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)`:
+
+
+```python
+@ovld
+def add(self, x: list, y: list):
+ return [self(a, b) for a, b in zip(x, y)]
+
+@ovld
+def add(self, x: tuple, y: tuple):
+ return tuple(self(a, b) for a, b in zip(x, y))
+
+@ovld
+def add(self, x: dict, y: dict):
+ return {k: self(v, y[k]) for k, v in x.items()}
+
+@ovld
+def add(self, x: object, y: object):
+ return x + y
+```
+
+Why is this useful, though? Observe:
+
+```python
+@add.variant
+def mul(self, x: object, y: object):
+ return x * y
+
+assert add([1, 2], [3, 4]) == [4, 6]
+assert mul([1, 2], [3, 4]) == [3, 8]
+```
+
+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.
+
+## State
+
+You can pass `initial_state` to `@ovld` or `variant`. The initial state must be a function that takes no arguments. Its return value will be available in `self.state`. The state is initialized at the top level call, but recursive calls to `self` will preserve it.
+
+In other words, you can do something like this:
+
+```python
+@add.variant(initial_state=lambda: 0)
+def count(self, x, y):
+ self.state += 1
+ return (f"#{self.state}", x + y)
+
+assert count([1, 2, 3], [4, 5, 6]) == [("#1", 5), ("#2", 7), ("#3", 9)]
+```
+
+The initial_state function can return any object and you can use the state to any purpose (e.g. cache or memoization).
+
+## Custom dispatch
+
+You can define your own dispatching function. The dispatcher's first argument is always `self`.
+
+* `self.resolve(x, y)` to get the right function for the types of x and y
+* `self[type(x), type(y)]` will also return the right function for these types, but it works directly with the types.
+
+For example, here is how you might define a function such that f(x) <=> f(x, x):
+
+```python
+@ovld.dispatch
+def add_default(self, x, y=None):
+ if y is None:
+ y = x
+ return self.resolve(x, y)(x, y)
+
+@ovld
+def add_default(x: int, y: int):
+ return x + y
+
+@ovld
+def add_default(x: str, y: str):
+ return x + y
+
+@ovld
+def add_default(xs: list, ys: list):
+ return [add_default(x, y) for x, y in zip(xs, ys)]
+
+assert add_default([1, 2, "alouette"]) == [2, 4, "alouettealouette"]
+```
+
+There are other uses for this feature, e.g. memoization.
+
+The normal functions may also have a `self`, which works the same as bootstrapping, and you can give an `initial_state` to `@ovld.dispatch` as well.
+
+## Postprocess
+
+`@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.
+
+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.
+
+## Methods
+
+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.
+
+```python
+from ovld import OvldMC
+
+class Cat(metaclass=OvldMC):
+ def interact(self, x: Mouse):
+ return "catch"
+
+ def interact(self, x: Food):
+ return "devour"
+
+ def interact(self, x: PricelessVase):
+ return "destroy"
+```
+
+Subclasses of `Cat` will inherit the overloaded `interact` and it may define additional overloaded methods which will only be valid for the subclass.
+
+**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:
+
+```python
+class Stuff(metaclass=OvldMC):
+ def __init__(self, mul):
+ self.mul = mul
+
+ @ovld.dispatch
+ def calc(ovldcall, x):
+ # Wraps every call to self.calc, but we receive ovldcall instead of self
+ # ovldcall[type(x)] returns the right method to call
+ # ovldcall.obj is the self (the actual instance of Stuff)
+ return ovldcall[type(x)](x) * ovldcall.obj.mul
+
+ def calc(self, x: int):
+ return x + 1
+
+ def calc(self, xs: list):
+ return [self.calc(x) for x in xs]
+
+print(Stuff(2).calc([1, 2, 3])) # [4, 6, 8, 4, 6, 8]
+```
+
+### Mixins in subclasses
+
+The `@extend_super` decorator on a method will combine the method with the definition on the superclass:
+
+```python
+from ovld import OvldMC, extend_super
+
+class One(metaclass=OvldMC):
+ def f(self, x: int):
+ return "an integer"
+
+class Two(One):
+ @extend_super
+ def f(self, x: str):
+ return "a string"
+
+assert Two().f(1) == "an integer"
+assert Two().f("s") == "a string"
+```
+
+## Ambiguous calls
+
+The following definitions will cause a TypeError at runtime when called with two ints, because it is unclear which function is the right match:
+
+```python
+@ovld
+def ambig(x: int, y: object):
+ print("io")
+
+@ovld
+def ambig(x: object, y: int):
+ print("oi")
+
+ambig(8, 8) # ???
+```
+
+You may define an additional function with signature (int, int) to disambiguate:
+
+```python
+@ovld
+def ambig(x: int, y: int):
+ print("ii")
+```
+
+## Other features
+
+### meta
+
+To test arbitrary conditions, you can use `meta`:
+
+```python
+from ovld import ovld, meta
+
+@meta
+def StartsWithT(cls):
+ return cls.__name__.startswith("T")
+
+@ovld
+def f(x: StartsWithT):
+ return "T"
+
+assert f(TypeError("xyz")) == "T"
+
+
+# Or: a useful example, since dataclasses have no common superclass:
+
+from dataclasses import dataclass, is_dataclass
+
+@dataclass
+class Point:
+ x: int
+ y: int
+
+@ovld
+def f(x: meta(is_dataclass)):
+ return "dataclass"
+
+assert f(Point(1, 2)) == "dataclass"
+```
+
+
+### deferred
+
+You may define overloads for certain classes from external packages without
+having to import them:
+
+
+```python
+from ovld import ovld, deferred
+
+@ovld
+def f(x: deferred("numpy.ndarray")):
+ return "ndarray"
+
+# numpy is not imported
+assert "numpy" not in sys.modules
+
+# But once we import it, the ovld works:
+import numpy
+assert f(numpy.arange(10)) == "ndarray"
+```
+
+
+### Tracebacks
+
+`ovld` automagically renames functions so that the stack trace is more informative:
+
+```python
+@add.variant
+def bad(self, x: object, y: object):
+ raise Exception("Bad.")
+
+bad([1], [2])
+
+"""
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 148, in bad.entry
+ res = ovc(*args, **kwargs)
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
+ return method(self.bind_to, *args, **kwargs)
+ File "example.py", line 6, in bad[list, list]
+ return [self(a, b) for a, b in zip(x, y)]
+ File "example.py", line 6, in <listcomp>
+ return [self(a, b) for a, b in zip(x, y)]
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
+ return method(self.bind_to, *args, **kwargs)
+ File "example.py", line 26, in bad[*, *]
+ raise Exception("Bad.")
+ Exception: Bad.
+"""
+```
+
+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.
+
+This also means profilers will be able to differentiate between these paths and between variants, even if they share code paths.
+
+
+%package help
+Summary: Development documents and examples for ovld
+Provides: python3-ovld-doc
+%description help
+
+# Ovld
+
+Multiple dispatch in Python, with some extra features.
+
+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.
+
+Other features of `ovld`:
+
+* Multiple dispatch for methods (with `metaclass=ovld.OvldMC`)
+* Create variants of functions
+* Built-in support for extensible, stateful recursion
+* Function wrappers
+* Function postprocessors
+* Nice stack traces
+
+## Example
+
+Here's a function that adds lists, tuples and dictionaries:
+
+```python
+from ovld import ovld
+
+@ovld
+def add(x: list, y: list):
+ return [add(a, b) for a, b in zip(x, y)]
+
+@ovld
+def add(x: tuple, y: tuple):
+ return tuple(add(a, b) for a, b in zip(x, y))
+
+@ovld
+def add(x: dict, y: dict):
+ return {k: add(v, y[k]) for k, v in x.items()}
+
+@ovld
+def add(x: object, y: object):
+ return x + y
+```
+
+## Bootstrapping and variants
+
+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)`:
+
+
+```python
+@ovld
+def add(self, x: list, y: list):
+ return [self(a, b) for a, b in zip(x, y)]
+
+@ovld
+def add(self, x: tuple, y: tuple):
+ return tuple(self(a, b) for a, b in zip(x, y))
+
+@ovld
+def add(self, x: dict, y: dict):
+ return {k: self(v, y[k]) for k, v in x.items()}
+
+@ovld
+def add(self, x: object, y: object):
+ return x + y
+```
+
+Why is this useful, though? Observe:
+
+```python
+@add.variant
+def mul(self, x: object, y: object):
+ return x * y
+
+assert add([1, 2], [3, 4]) == [4, 6]
+assert mul([1, 2], [3, 4]) == [3, 8]
+```
+
+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.
+
+## State
+
+You can pass `initial_state` to `@ovld` or `variant`. The initial state must be a function that takes no arguments. Its return value will be available in `self.state`. The state is initialized at the top level call, but recursive calls to `self` will preserve it.
+
+In other words, you can do something like this:
+
+```python
+@add.variant(initial_state=lambda: 0)
+def count(self, x, y):
+ self.state += 1
+ return (f"#{self.state}", x + y)
+
+assert count([1, 2, 3], [4, 5, 6]) == [("#1", 5), ("#2", 7), ("#3", 9)]
+```
+
+The initial_state function can return any object and you can use the state to any purpose (e.g. cache or memoization).
+
+## Custom dispatch
+
+You can define your own dispatching function. The dispatcher's first argument is always `self`.
+
+* `self.resolve(x, y)` to get the right function for the types of x and y
+* `self[type(x), type(y)]` will also return the right function for these types, but it works directly with the types.
+
+For example, here is how you might define a function such that f(x) <=> f(x, x):
+
+```python
+@ovld.dispatch
+def add_default(self, x, y=None):
+ if y is None:
+ y = x
+ return self.resolve(x, y)(x, y)
+
+@ovld
+def add_default(x: int, y: int):
+ return x + y
+
+@ovld
+def add_default(x: str, y: str):
+ return x + y
+
+@ovld
+def add_default(xs: list, ys: list):
+ return [add_default(x, y) for x, y in zip(xs, ys)]
+
+assert add_default([1, 2, "alouette"]) == [2, 4, "alouettealouette"]
+```
+
+There are other uses for this feature, e.g. memoization.
+
+The normal functions may also have a `self`, which works the same as bootstrapping, and you can give an `initial_state` to `@ovld.dispatch` as well.
+
+## Postprocess
+
+`@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.
+
+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.
+
+## Methods
+
+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.
+
+```python
+from ovld import OvldMC
+
+class Cat(metaclass=OvldMC):
+ def interact(self, x: Mouse):
+ return "catch"
+
+ def interact(self, x: Food):
+ return "devour"
+
+ def interact(self, x: PricelessVase):
+ return "destroy"
+```
+
+Subclasses of `Cat` will inherit the overloaded `interact` and it may define additional overloaded methods which will only be valid for the subclass.
+
+**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:
+
+```python
+class Stuff(metaclass=OvldMC):
+ def __init__(self, mul):
+ self.mul = mul
+
+ @ovld.dispatch
+ def calc(ovldcall, x):
+ # Wraps every call to self.calc, but we receive ovldcall instead of self
+ # ovldcall[type(x)] returns the right method to call
+ # ovldcall.obj is the self (the actual instance of Stuff)
+ return ovldcall[type(x)](x) * ovldcall.obj.mul
+
+ def calc(self, x: int):
+ return x + 1
+
+ def calc(self, xs: list):
+ return [self.calc(x) for x in xs]
+
+print(Stuff(2).calc([1, 2, 3])) # [4, 6, 8, 4, 6, 8]
+```
+
+### Mixins in subclasses
+
+The `@extend_super` decorator on a method will combine the method with the definition on the superclass:
+
+```python
+from ovld import OvldMC, extend_super
+
+class One(metaclass=OvldMC):
+ def f(self, x: int):
+ return "an integer"
+
+class Two(One):
+ @extend_super
+ def f(self, x: str):
+ return "a string"
+
+assert Two().f(1) == "an integer"
+assert Two().f("s") == "a string"
+```
+
+## Ambiguous calls
+
+The following definitions will cause a TypeError at runtime when called with two ints, because it is unclear which function is the right match:
+
+```python
+@ovld
+def ambig(x: int, y: object):
+ print("io")
+
+@ovld
+def ambig(x: object, y: int):
+ print("oi")
+
+ambig(8, 8) # ???
+```
+
+You may define an additional function with signature (int, int) to disambiguate:
+
+```python
+@ovld
+def ambig(x: int, y: int):
+ print("ii")
+```
+
+## Other features
+
+### meta
+
+To test arbitrary conditions, you can use `meta`:
+
+```python
+from ovld import ovld, meta
+
+@meta
+def StartsWithT(cls):
+ return cls.__name__.startswith("T")
+
+@ovld
+def f(x: StartsWithT):
+ return "T"
+
+assert f(TypeError("xyz")) == "T"
+
+
+# Or: a useful example, since dataclasses have no common superclass:
+
+from dataclasses import dataclass, is_dataclass
+
+@dataclass
+class Point:
+ x: int
+ y: int
+
+@ovld
+def f(x: meta(is_dataclass)):
+ return "dataclass"
+
+assert f(Point(1, 2)) == "dataclass"
+```
+
+
+### deferred
+
+You may define overloads for certain classes from external packages without
+having to import them:
+
+
+```python
+from ovld import ovld, deferred
+
+@ovld
+def f(x: deferred("numpy.ndarray")):
+ return "ndarray"
+
+# numpy is not imported
+assert "numpy" not in sys.modules
+
+# But once we import it, the ovld works:
+import numpy
+assert f(numpy.arange(10)) == "ndarray"
+```
+
+
+### Tracebacks
+
+`ovld` automagically renames functions so that the stack trace is more informative:
+
+```python
+@add.variant
+def bad(self, x: object, y: object):
+ raise Exception("Bad.")
+
+bad([1], [2])
+
+"""
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 148, in bad.entry
+ res = ovc(*args, **kwargs)
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
+ return method(self.bind_to, *args, **kwargs)
+ File "example.py", line 6, in bad[list, list]
+ return [self(a, b) for a, b in zip(x, y)]
+ File "example.py", line 6, in <listcomp>
+ return [self(a, b) for a, b in zip(x, y)]
+ File "/Users/breuleuo/code/ovld/ovld/core.py", line 182, in bad.dispatch
+ return method(self.bind_to, *args, **kwargs)
+ File "example.py", line 26, in bad[*, *]
+ raise Exception("Bad.")
+ Exception: Bad.
+"""
+```
+
+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.
+
+This also means profilers will be able to differentiate between these paths and between variants, even if they share code paths.
+
+
+%prep
+%autosetup -n ovld-0.3.2
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-ovld -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Wed May 31 2023 Python_Bot <Python_Bot@openeuler.org> - 0.3.2-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..cd2c274
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+7d8c5c9ac18b43d12042298a2c0ecf42 ovld-0.3.2.tar.gz