RussBaz / enforce

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

Handling of generator/iterables/coroutines/... #47

Open kevinboulain opened 6 years ago

kevinboulain commented 6 years ago

As requested, I am opening this issue instead of #44.

Seen with enforce 0.3.4.

Example code with a generator:

from typing import Generator
import enforce

@enforce.runtime_validation
def generator() -> Generator[int, None, None]:
    i = 0
    while True:
        yield i
        i += 1

g = generator()
print(next(g))
print(next(g))

An exception will occur in enforce:

Traceback (most recent call last):
  File ".../venv/lib/python3.6/site-packages/enforce/nodes.py", line 539, in preprocess_data
    enforcer = data.__enforcer__
AttributeError: 'generator' object has no attribute '__enforcer__'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "enforce_examples.py", line 11, in <module>
    g = generator()
  File ".../venv/lib/python3.6/site-packages/enforce/decorators.py", line 118, in universal
    return enforcer.validate_outputs(result)
  File ".../venv/lib/python3.6/site-packages/enforce/enforcers.py", line 96, in validate_outputs
    if not self.validator.validate(output_data, 'return'):
  File ".../venv/lib/python3.6/site-packages/enforce/validator.py", line 26, in validate
    validation_result = visit(validation_tree)
  File ".../venv/lib/python3.6/site-packages/enforce/utils.py", line 17, in visit
    stack.append(last.send(last_result))
  File ".../venv/lib/python3.6/site-packages/enforce/nodes.py", line 64, in validate
    clean_data = self.preprocess_data(validator, data)
  File ".../venv/lib/python3.6/site-packages/enforce/nodes.py", line 541, in preprocess_data
    return GenericProxy(data)
  File ".../venv/lib/python3.6/site-packages/enforce/enforcers.py", line 130, in __init__
    raise TypeError('Only generics can be wrapped in GenericProxy')
TypeError: Only generics can be wrapped in GenericProxy

There is a similar problem when a function returns a Coroutine.

This seems normal for now as there isn't any real support for these objects.

smarie commented 6 years ago

I seem to get the same exception when using typing.Sequence instead of typing.List

from typing import Sequence
from enforce import runtime_validation, config
config(dict(mode='covariant'))  # by the way, so sad that this is not the default :)

@runtime_validation
def foo(s: Sequence[str]):
    pass

foo(['a'])  # TypeError: Only generics can be wrapped in GenericProxy