diff options
author | CoprDistGit <infra@openeuler.org> | 2023-05-31 06:28:50 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-05-31 06:28:50 +0000 |
commit | 8ad4a4cb1337231780b7b111abba38615bf78a14 (patch) | |
tree | 4ec011a8d5575ec72f03a72bcbba2b52118d623b | |
parent | 9a6cb0a2f9d679ab0fc995f5be555f9f87a4b400 (diff) |
automatic import of python-ovld
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | python-ovld.spec | 1005 | ||||
-rw-r--r-- | sources | 1 |
3 files changed, 1007 insertions, 0 deletions
@@ -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 @@ -0,0 +1 @@ +7d8c5c9ac18b43d12042298a2c0ecf42 ovld-0.3.2.tar.gz |