RussBaz / enforce

Python 3.5+ runtime type checking for integration testing and data validation
543 stars 21 forks source link

"User defined generic does not accept provided constraints" error when 'Type' is used in hint #68

Open smarie opened 6 years ago

smarie commented 6 years ago

PEP484 allows TypeVar to be used anywhere, not necessarily in Generic classes, see this section.

However as of today the following fails:

from enforce import runtime_validation
from typing import TypeVar, Type

T = TypeVar('T', bound=int)

@runtime_validation
class Foo:
    def foo(self, arg: Type[T]) -> T:
        return arg()

With error:

(...)
  File "C:\Miniconda3\envs\baseenv\lib\site-packages\enforce\enforcers.py", line 206, in generate_new_enforcer
    raise TypeError('User defined generic does not accept provided constraints')
TypeError: User defined generic does not accept provided constraints
smarie commented 6 years ago

Note that this can be even reduced down to

@runtime_validation
def foo(self, arg: Type[T]) -> T:
    return arg()

Apparently that's the Type[T] that causes the problem, because changing it to T makes it work.

RussBaz commented 6 years ago

Is this happening in the dev branch as well? It might be related to the fact that Type construct is not properly supported yet. However, I will have a look into it later this week.

RussBaz commented 6 years ago

If it is actually a problem with Type only, can you rename the issue to reflect it? Thanks.

smarie commented 6 years ago

Please note that in early versions of typing.py module the Type symbol does not exist. Could you please make sure that it is not imported by enforce ? Otherwise new versions would break on our production environment (python 3.5.1, with no possibility to update the typing module using pip).

In all of my code to cope with this restriction I always quote Type when used in type hints, and add the following import:

try:
    from typing import Type
except ImportError:
    # normal for old versions of typing
    pass

Of course in your case you do want to reason about a type hint being the Type symbol, so I suggest the following import trick:

```python
try:
    from typing import Type
except ImportError:
    # normal for old versions of typing
    class Type:
        pass

I guess that should do the trick.