FelixTheC / strongtyping

Decorator which checks whether the function is called with the correct type of parameters.
https://pypi.org/project/strongtyping/
107 stars 3 forks source link

duck type compatability #93

Closed hjalmarlucius closed 2 years ago

hjalmarlucius commented 2 years ago

It would be great if there were a switch to allow duck type compatability - e.g. ints pass float checks. This would make the type checking consistent with mypy and mypyc. I would think the easiest solution would be to replace float->Real and int->Integral in checks. https://mypy.readthedocs.io/en/stable/duck_type_compatibility.html

hjalmarlucius commented 2 years ago

This function seems to work:

from numbers import Integral
from typing import get_origin
from typing import get_args

def replace_type(structure: Any, fromtype: type, totype: type) -> Any:
    if structure is fromtype:
        return totype
    if (origin := get_origin(structure)) is fromtype:
        structure.__origin__ = totype
    oldargs = get_args(structure)
    newargs = tuple(replace_type(arg, fromtype, totype) for arg in oldargs)
    if origin is not None and oldargs != newargs:
        return origin[newargs]
    return structure

# example
replace_type(dict[str, int], int, Integral)
FelixTheC commented 2 years ago

what about this??

from numbers import Integral, Real
Number = Union[Integral, Real]

@match_typing
def adder(x: int, y: Number):
    return x + y

# passes 
> adder(2, 2.5)
> adder(2, 5)
hjalmarlucius commented 2 years ago

Agree that works natively but that would not solve the conflict between native python and mypy/mypyc - so it would be the user that actively chooses to use Integral instead of int and Real instead of float:

x: float = 1  # mypy approves and mypyc compiles
assert isinstance(x, float)  # TypeError

What I was thinking was adding a match_duck_typing function:

@match_duck_typing
def adder(x: int, y: float):
    return x + y

# passes 
> adder(2, 2.5)
> adder(2, 5)
FelixTheC commented 2 years ago

Will your issue be solved with the following solution https://github.com/FelixTheC/strongtyping/blob/93-duck-type-compatability/docs/match_typing.md#allow-duck_typing??

hjalmarlucius commented 2 years ago

No, my point was that mypy checks some things differently than python's isinstance check - the below is ok according to mypy because it follows the numbers hierarchy but not at runtime as int is not a subclass of float.

x: float = 3
assert isinstance(x, float)
FelixTheC commented 2 years ago

These are the tests according to your Initial comment https://github.com/FelixTheC/strongtyping/blob/93-duck-type-compatability/strongtyping/tests/test_duck_typing.py

FelixTheC commented 2 years ago

Please feel free to create some too which I can add

hjalmarlucius commented 2 years ago

That looks like a very good implementation, thanks! (I didn't see the check_duck_typing change upon first answering)

FelixTheC commented 2 years ago

Ok then I will create a new release with this include.