RussBaz / enforce

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

`numbers` ABCs are not supported #50

Open grayfall opened 6 years ago

grayfall commented 6 years ago

I get an NameError when I try to use ABCs from the numbers module, e.g.

from typing import NamedTuple, Text, Tuple
from numbers import Integral

Interval = Tuple[Integral, Integral]
Annotation = NamedTuple("Annotation", [("source", Text), ("start", Integral),
                                       ("end", Integral), ("text", Text),
                                       ("cls", Text)])

Using these annotations in any function gives me:

Traceback (most recent call last):
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1599, in <module>
    globals = debugger.run(setup['file'], None, None, is_module)
  File "/Applications/PyCharm.app/Contents/helpers/pydev/pydevd.py", line 1026, in run
    pydev_imports.execfile(file, globals, locals)  # execute the script
  File "/Applications/PyCharm.app/Contents/helpers/pydev/_pydev_imps/_pydev_execfile.py", line 18, in execfile
    exec(compile(contents+"\n", file, 'exec'), glob, loc)
  File "/Applications/PyCharm.app/Contents/helpers/pycharm/docrunner.py", line 297, in <module>
    modules = [loadSource(a[0])]
  File "/Applications/PyCharm.app/Contents/helpers/pycharm/docrunner.py", line 229, in loadSource
    module = imp.load_source(moduleName, fileName)
  File "/Users/ilia/.venvs/py3/lib/python3.5/imp.py", line 172, in load_source
    module = _load(spec)
  File "<frozen importlib._bootstrap>", line 693, in _load
  File "<frozen importlib._bootstrap>", line 673, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 665, in exec_module
  File "<frozen importlib._bootstrap>", line 222, in _call_with_frames_removed
  File "/Users/ilia/OneDrive/GitHub/skoblov-lab/chempred/chempred/chemdner.py", line 52, in <module>
    -> List[Tuple[int, List[Annotation], List[Annotation]]]:
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/decorators.py", line 58, in runtime_validation
    return generate_decorated()
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/decorators.py", line 158, in build_wrapper
    return decorate(wrapped, configuration, None)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/decorators.py", line 70, in decorate
    data = apply_enforcer(data, parent_root=parent_root, settings=configuration)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/enforcers.py", line 159, in apply_enforcer
    func.__enforcer__ = generate_new_enforcer(func, generic, parent_root, instance_of, settings)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/enforcers.py", line 227, in generate_new_enforcer
    validator = init_validator(hints, parent_root)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/validator.py", line 71, in init_validator
    syntax_tree = visit(root_parser)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/utils.py", line 17, in visit
    stack.append(last.send(last_result))
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/parsers.py", line 54, in _parse_namedtuple
    new_node = yield nodes.NamedTupleNode(hint)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/nodes.py", line 390, in __init__
    super().__init__(runtime_validation(data_type), is_sequence=True, is_container=True, **kwargs)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/decorators.py", line 46, in runtime_validation
    return get_typed_namedtuple(configuration, data, fields, field_types)
  File "/Users/ilia/.venvs/py3/lib/python3.5/site-packages/enforce/decorators.py", line 180, in get_typed_namedtuple
    exec(new_init_template, context)
  File "<string>", line 1, in <module>
NameError: name 'Integral' is not defined
TheDataLeek commented 6 years ago

So if you type using just Integral, it works fine:

@runtime_validation
def foo(x: Integral) -> None:
    return

Meaning this is some weird interaction between NamedTuple and Integral.... Will keep digging.

RussBaz commented 6 years ago

I think I have nailed this issue. I used to create typed NamedTuples using exec from a string template. Unfortunately, it was hard to provide correct context for exec under such an approach. I have scraped it. It should work in dev now.

Please remember that I have updated the dev installation instructions:

git clone https://github.com/RussBaz/enforce.git@dev
cd enforce
# Please use a virtual environment before proceeding!
pip install -r requirements.txt
pip install -e .