Type hinting in Python 2
According to Suggested syntax for Python 2.7 and straddling code in PEP 484 which defined type hinting, there is an alternative syntax for compatibility with Python 2.7. It is however not mandatory so I don't know how well supported it is, but quoting the PEP:
Some tools may want to support type annotations in code that must be compatible with Python 2.7. For this purpose this PEP has a suggested (but not mandatory) extension where function annotations are placed in a # type: comment. Such a comment must be placed immediately following the function header (before the docstring). An example: the following Python 3 code:
def embezzle(self, account: str, funds: int = 1000000, *fake_receipts: str) -> None: """Embezzle funds from account using fake receipts.""" <code goes here>
is equivalent to the following:
def embezzle(self, account, funds=1000000, *fake_receipts): # type: (str, int, *str) -> None """Embezzle funds from account using fake receipts.""" <code goes here>
For mypy
support, see Type checking Python 2 code.
At this point the recommended and python3 compatible way to do is to follow the python2 to 3 guide : http://python-future.org/func_annotations.html
def embezzle(self, account: str, funds: int = 1000000, *fake_receipts: str) -> None:
"""Embezzle funds from account using fake receipts."""
pass
Become:
def embezzle(self, account, funds = 1000000, *fake_receipts):
"""Embezzle funds from account using fake receipts."""
pass
embezzle.__annotations__ = {'account': str, 'funds': int, 'fake_receipts': str, 'return': None}
Here is a function i wrote to parse the Python 2 type comment and get a tuple of input types and the return type. It would need some work to work with complex type definitions from the typing library (Any, Optional, List, etc.):
class InvalidTypeHint(Exception):
pass
PYTHON_2_TYPE_HINT_REGEX = "\s*#\s*type:\s*(\(.+\))\s*->\s*(.+)\s*"
def parse_python_2_type_hint(typehint_string):
# type: (str) -> (tuple, type)
pattern = re.compile(PYTHON_2_TYPE_HINT_REGEX)
search_results = pattern.search(typehint_string)
if not search_results:
raise InvalidTypeHint('%s does not match type hint spec regex %s' % (typehint_string, PYTHON_2_TYPE_HINT_REGEX))
arg_types_str = search_results.group(1)
return_type_str = search_results.group(2)
try:
arg_types_tuple = eval(arg_types_str)
assert isinstance(arg_types_tuple, tuple)
return_type = eval(return_type_str)
assert isinstance(return_type, type)
except Exception as e:
raise InvalidTypeHint(e)
return arg_types_tuple, return_type
def parse_arg_types_for_callable(func):
# type:(callable)->tuple
"""
:param func:
:return: list of parameter types if successfully parsed, else None
"""
# todo make this compatible with python 3 type hints
# python 2.7 type hint
source_lines = inspect.getsource(func).split("\n")
def_statements = 0
for source_line in source_lines:
try:
arg_types_tuple, return_type = parse_python_2_type_hint(source_line)
return arg_types_tuple
except InvalidTypeHint:
if source_line.strip().startswith("def "):
def_statements += 1
if def_statements > 1:
return None