XXX.monkey
dynamically types interactive code.¶
pip install monkeytype retype
XXX.monkey
uses Instagram's to trace function calls
in a running Jupyter instance or __import__('__main__')
.
XXX.monkey
uses @ambv's retype
to automatically annotate a cell input with types.
The original is a gist.
from monkeytype.tracing import CallTraceLogger as Logger, trace_calls
from monkeytype.stubs import build_module_stubs_from_traces
DUNDER = '__%s__'
XXX.IPythonTrace
is a monkeytype.CallTraceLogger
¶The monkeytype.CallTraceLogger
stores logged traces in a monkeytype.CallTraceStore
class Tracer(Logger):
def stubs(logger, modules=None, main=True):
modules = modules or []
if main: modules += [DUNDER%'main']
stubs = build_module_stubs_from_traces(logger.data)
return '\n'.join(stubs.get(module).render() for module in modules)
def __enter__(Logger):
Logger.ctx = trace_calls(Logger)
Logger.ctx.__enter__()
return Logger
def __exit__(Logger, *args, **kwargs): Logger.ctx.__exit__(*args, **kwargs)
def __init__(Logger, data=None): Logger.data, Logger.traces = None or [], []
def log(Logger, trace): Logger.traces.append(trace)
def flush(Logger): Logger.traces = Logger.data.extend(Logger.traces) or []
def monkey(line, cell):
"""A IPython magic to trace the types of function calls."""
ip=__import__('IPython').get_ipython()
with Tracer() as logger: exec(compile(cell, '<typing>', 'exec'), ip.user_ns, ip.user_ns)
if line.strip():
# A special argument to Retype the functions expressed in the cell
if 'retype' in line: ip.set_next_input(retype(logger))
else:
# Apply the %%file magic parameters if line exists.
ip.magics_manager.magics['cell']['file'](line, logger.stubs() + '\n')
else:
# Other show the stub in the next call.
ip.set_next_input(logger.stubs())
def retype(logger):
"""
>>> def f(x): return range(x), str(x), int(x)
"""
import retype
src = '\n'.join(map(
__import__('inspect').getsource, set(_.func for _ in logger.data if hasattr(_, 'func'))))
retype.Config.incremental, retype.Config.replace_any = False, True
src = retype.lib2to3_parse(src)
retype.reapply(__import__('typed_ast').ast3.parse(logger.stubs()).body, src)
retype.fix_remaining_type_comments(src)
return retype.lib2to3_unparse(src, hg=False)
def load_ipython_extension(ip=__import__('IPython').get_ipython()):
ip.register_magic_function(monkey, 'cell')
if __name__ == '__main__':
load_ipython_extension()
print('✅🚫'[bool(__import__('doctest').testmod().failed)])
!source activate p6 && pytest tests/test_typing.ipynb