def _get_type_hints(func, args = None, res = None, infer_defaults = None):
"""Helper for get_type_hints.
"""
if args is None or res is None:
args2, res2 = _get_types(func, util.is_classmethod(func),
util.is_method(func), unspecified_type = type(NotImplemented),
infer_defaults = infer_defaults)
if args is None:
args = args2
if res is None:
res = res2
slf = 1 if util.is_method(func) else 0
argNames = util.getargnames(util.getargspecs(util._actualfunc(func)))
result = {}
if not args is Any:
prms = get_Tuple_params(args)
for i in range(slf, len(argNames)):
if not prms[i-slf] is type(NotImplemented):
result[argNames[i]] = prms[i-slf]
result['return'] = res
return result
python类get_type_hints()的实例源码
def check_arguments(c: typing.Callable,
hints: typing.Mapping[str, typing.Optional[type]],
*args, **kwargs) -> None:
"""Check arguments type, raise :class:`TypeError` if argument type is not
expected type.
:param c: callable object want to check types
:param hints: assumed type of given ``c`` result of
:func:`typing.get_type_hints`
"""
signature = inspect.signature(c)
bound = signature.bind(*args, **kwargs)
for argument_name, value in bound.arguments.items():
try:
type_hint = hints[argument_name]
except KeyError:
continue
actual_type, correct = check_type(value, type_hint)
if not correct:
raise TypeError(
'Incorrect type `{}`, expected `{}` for `{}`'.format(
actual_type, type_hint, argument_name
)
)
def test_various(self):
self.assertEqual(get_type_hints(testfunc),
{'a': int, 'c': str, 'b': Real, 'return': Tuple[int, Real]})
self.assertEqual(pytypes.deep_type(('abc', [3, 'a', 7], 4.5)),
Tuple[str, List[Union[int, str]], float])
tc2 = testClass2('bbb')
self.assertEqual(pytypes.get_class_that_defined_method(
tc2.testmeth2c), testClass2)
self.assertEqual(pytypes.get_class_that_defined_method(
testClass2.testmeth2c), testClass2)
self.assertEqual(pytypes.get_class_that_defined_method(
tc2.testmeth2b), testClass2)
self.assertEqual(pytypes.get_class_that_defined_method(
testClass2.testmeth2b), testClass2)
self.assertEqual(pytypes.get_class_that_defined_method(
tc2.testmeth3), testClass2)
self.assertEqual(pytypes.get_class_that_defined_method(
testClass2.testmeth3), testClass2)
self.assertRaises(ValueError, lambda:
pytypes.get_class_that_defined_method(testfunc))
# old-style:
tc3 = testClass3()
self.assertEqual(pytypes.get_class_that_defined_method(
tc3.testmeth), testClass3)
self.assertEqual(pytypes.get_class_that_defined_method(
testClass3.testmeth), testClass3)
def test_method_forward_py3(self):
tc = py3.testClass('ijkl2')
tc2 = py3.testClass2('ijkl3')
self.assertEqual(tc.testmeth_forward(5, tc2), 11)
self.assertEqual(typing.get_type_hints(tc.testmeth_forward),
get_type_hints(tc.testmeth_forward))
self.assertRaises(InputTypeError, lambda: tc.testmeth_forward(5, 7))
self.assertRaises(InputTypeError, lambda: tc.testmeth_forward(5, tc))
def test_various_py3(self):
self.assertEqual(get_type_hints(testfunc),
{'a': int, 'c': str, 'b': Real, 'return': Tuple[int, Real]})
self.assertEqual(pytypes.deep_type(('abc', [3, 'a', 7], 4.5)),
Tuple[str, List[Union[int, str]], float])
def get_types(func):
"""Works like get_type_hints, but returns types as a sequence rather than a
dictionary. Types are returned in declaration order of the corresponding arguments.
"""
return _get_types(func, util.is_classmethod(func), util.is_method(func))
def get_type_hints(func):
"""Resembles typing.get_type_hints, but is also workable on Python 2.7 and
searches stubfiles for type information.
Also on Python 3, this takes type comments
(python.org/dev/peps/pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code)
into account if present.
"""
if not has_type_hints(func):
# What about defaults?
return {}
return _get_type_hints(func)
def check_callable(callable_: typing.Callable, hint: type) -> bool:
"""Check argument type & return type of :class:`typing.Callable`. since it
raises check :class:`typing.Callable` using `isinstance`, so compare in
diffrent way
:param callable_: callable object given as a argument
:param hint: assumed type of given ``callable_``
"""
if not callable(callable_):
return type(callable_), False
if callable(callable_) and not hasattr(callable_, '__code__'):
return type(callable_), True
hints = typing.get_type_hints(callable_)
return_type = hints.pop('return', type(None))
signature = inspect.signature(callable_)
arg_types = tuple(
param.annotation
for _, param in signature.parameters.items()
)
correct = all({
any({
hint.__args__ is None,
hint.__args__ is Ellipsis,
hint.__args__ == arg_types,
}),
any({
hint.__result__ is None,
hint.__result__ in (typing.Any, return_type)
})
})
return typing.Callable[list(arg_types), return_type], correct
def typechecked(call_: typing.Callable[..., T]) -> T:
"""A decorator to make a callable object checks its types
.. code-block:: python
from typing import Callable
@typechecked
def foobar(x: str) -> bool:
return x == 'hello world'
@typechecked
def hello_world(foo: str, bar: Callable[[str], bool]) -> bool:
return bar(foo)
hello_world('hello world', foobar)
hello_world(3.14, foobar) # it raise TypeError
:param c: callable object want to check types
:type c: :class:`typing.Callable`
:return:
"""
@functools.wraps(call_)
def decorator(*args, **kwargs):
hints = typing.get_type_hints(call_)
check_arguments(call_, hints, *args, **kwargs)
result = call_(*args, **kwargs)
check_return(call_.__name__, result, hints)
return result
return decorator
def _get_type_hints(obj, globalns=None, localns=None):
return getattr(obj, '__annotations__', {})
def __call__(self, function):
if not self.shouldCheck:
return function
hints = get_type_hints(function)
def precheck(*args, **kwargs):
all_args = kwargs.copy()
all_args.update(dict(zip(function.__code__.co_varnames, args)))
for argument, argument_type in ((i, type(j)) for i, j in all_args.items()):
if argument in hints:
if not issubclass(argument_type, hints[argument]):
raise TypeError('Type of {} is {} and not {}'.
format(argument,
argument_type,
hints[argument]))
def postcheck(result):
if 'return' in hints:
if not isinstance(result, hints['return']):
raise TypeError('Type of result is {} and not {}'.
format(type(result), hints['return']))
return result
if inspect.iscoroutinefunction(function):
async def type_checker(*args, **kwargs):
precheck(*args, **kwargs)
result = await function(*args, **kwargs)
return postcheck(result)
else:
def type_checker(*args, **kwargs):
precheck(*args, **kwargs)
result = function(*args, **kwargs)
return postcheck(result)
return type_checker
def _get_func_type_annotations(func: Callable[..., Any], owner: type = None) -> Dict[str, type]:
annotations = dict(get_type_hints(func))
dynamic_annotations = getattr(func, '__dynamic__', {})
for name, expression in dynamic_annotations.items():
if type(expression) == type:
annotations[name] = expression
elif callable(expression):
annotations[name] = expression(owner)
return annotations
# TODO name arg for use with auto-generation
def get_type_hints(obj, # type: Any
globalns=None, # type: Optional[Dict[str, Any]]
localns=None # type: Optional[Dict[str, Any]]
):
# type: (...) -> Dict[str, Any]
"""Return all type hints for the function.
This attempts to use typing.get_type_hints first, but if that returns None
then it will attempt to reuse much of the logic from the Python 3 version
of typing.get_type_hints; the Python 2 version does nothing. In addition to
this logic, if no code annotations exist, it will attempt to extract
comment type hints for Python 2/3 compatibility.
Args:
obj: The object to search for type hints.
globalns: The currently known global namespace.
localns: The currently known local namespace.
Returns:
A mapping of value names to type hints.
"""
hints = {}
try:
if not isinstance(obj, type):
hints = _get_type_hints(obj, globalns, localns) or {}
except TypeError:
if not isinstance(obj, _STRING_TYPES):
raise
if not hints and not getattr(obj, '__no_type_check__', None):
globalns, localns = _get_namespace(obj, globalns, localns)
hints = _get_comment_type_hints(obj, globalns, localns)
for name, value in six.iteritems(hints):
if value is None:
value = type(None)
elif isinstance(value, _STRING_TYPES):
value = _ForwardRef(value)
hints[name] = _eval_type(value, globalns, localns)
return hints
def has_return_hint(function):
"""
has_return_hint(function)
Returns whether function has a return type hint.
"""
return "return" in typing.get_type_hints(function)
def callback_types(self):
return get_type_hints(self.callback)
def __init__(self, *args):
if len(args) == 1:
wrapped_prop = args[0]
super().__init__(wrapped_prop.fget, self.wrap_setter(wrapped_prop.fset), wrapped_prop.fdel)
self.wrapped_property = wrapped_prop
self.bindings = []
self.observers = []
self.dtype = typing.get_type_hints(wrapped_prop.fget)['return']
else:
super().__init__(*args)
def autocast_decorator(type_hint, fix_arg_func):
"""
Decorator which will invoke fix_arg_func for any
arguments annotated with type_hint. The decorated
function will then be called with the result.
:param type_hint: A PEP484 type hint
:param fix_arg_func: Function to invoke
:return: decorator
"""
@decorator
def wrapper(wrapped, instance, args, kwargs):
hinted_args = names = None
cache_key = '%s-%s-%s' % (wrapped.__class__.__name__,
wrapped.__name__, str(type_hint))
if cache_key in AUTOCAST_CACHE:
hinted_args, names = AUTOCAST_CACHE[cache_key]
else:
sig = inspect.signature(wrapped)
names = list(sig.parameters.keys())
hinted_args = [x[0] for x in typing.get_type_hints(wrapped).items() \
if x[1] == type_hint or x[1] == typing.Union[type_hint, None]]
AUTOCAST_CACHE[cache_key] = hinted_args, names
if len(hinted_args) == 0:
raise ValueError("No arguments with %s hint found" % type_hint)
new_args = list(args)
for hinted_arg in hinted_args:
if hinted_arg in kwargs:
kwargs[hinted_arg] = fix_arg_func(kwargs[hinted_arg])
elif hinted_arg in names:
idx = names.index(hinted_arg)
if idx < len(new_args):
new_args[idx] = fix_arg_func(new_args[idx])
return wrapped(*new_args, **kwargs)
return wrapper
def test_defaults_inferred_types(self):
tmp = pytypes.infer_default_value_types
pytypes.infer_default_value_types = True
self.assertEqual(get_types(func_defaults_typecheck),
(Tuple[str, Any, int, float], str))
self.assertEqual(pytypes.get_type_hints(func_defaults_typecheck),
{'a': str, 'c': int, 'return': str, 'd': float})
self.assertEqual(func_defaults_typecheck('qvw', 'abc', 2, 1.5), 'qvwabcabc')
self.assertRaises(InputTypeError, lambda:
func_defaults_typecheck('qvw', 'abc', 3.5))
self.assertEqual(func_defaults_typecheck2('test', 12.2, 123), 'test1500.6False')
self.assertRaises(InputTypeError, lambda:
func_defaults_typecheck2('test', 12.2, 123, 3.5))
self.assertRaises(InputTypeError, lambda:
func_defaults_typecheck('qvw', 'abc', 3.5, 4.1))
self.assertRaises(InputTypeError, lambda: func_defaults_typecheck(7, 'qvw'))
self.assertEqual(func_defaults_checkargs('qvw', 'abc', 3, 1.5), 'qvwabcabcabc')
self.assertRaises(InputTypeError, lambda:
func_defaults_checkargs('qvw', 'abc', 3.5))
self.assertRaises(InputTypeError, lambda:
func_defaults_checkargs('qvw', 'abc', 3.5, 4.1))
self.assertRaises(InputTypeError, lambda: func_defaults_checkargs(7, 'qvw'))
self.assertEqual(get_types(func_defaults_annotations),
(Tuple[str, Any, int], str))
self.assertEqual(pytypes.get_type_hints(func_defaults_annotations),
{'a': str, 'c': int, 'return': str})
self.assertEqual(func_defaults_annotations.__annotations__,
{'a': str, 'return': str})
pytypes.infer_default_value_types = False
self.assertEqual(get_types(func_defaults_typecheck),
(Tuple[str, Any, Any, Any], str))
self.assertEqual(pytypes.get_type_hints(func_defaults_typecheck),
{'a': str, 'return': str})
self.assertEqual(func_defaults_typecheck('qvw', 'abc', 3.5), 'invalid')
self.assertEqual(func_defaults_typecheck('qvw', 'abc', 3.5, 4.1), 'invalid')
self.assertRaises(InputTypeError, lambda: func_defaults_typecheck(7, 'qvw'))
self.assertEqual(func_defaults_checkargs('qvw', 'abc', 3, 1.5), 'qvwabcabcabc')
self.assertEqual(func_defaults_checkargs('qvw', 'abc', 3.5), 'invalid')
self.assertEqual(func_defaults_checkargs('qvw', 'abc', 3.5, 4.1), 'invalid')
self.assertRaises(InputTypeError, lambda: func_defaults_checkargs(7, 'qvw'))
self.assertEqual(get_types(func_defaults_annotations),
(Tuple[str, Any, Any], str))
self.assertEqual(pytypes.get_type_hints(func_defaults_annotations),
{'a': str, 'return': str})
self.assertEqual(func_defaults_annotations.__annotations__,
{'a': str, 'return': str})
pytypes.infer_default_value_types = tmp
def test_defaults_inferred_types(self):
tmp = pytypes.infer_default_value_types
pytypes.infer_default_value_types = True
self.assertEqual(get_types(py3.func_defaults_typecheck),
(Tuple[str, Any, int, float], str))
self.assertEqual(pytypes.get_type_hints(py3.func_defaults_typecheck),
{'a': str, 'c': int, 'return': str, 'd': float})
self.assertEqual(py3.func_defaults_typecheck('qvw', 'abc', 2, 1.5), 'qvwabcabc')
self.assertRaises(InputTypeError, lambda:
py3.func_defaults_typecheck('qvw', 'abc', 3.5))
self.assertEqual(py3.func_defaults_typecheck2('test', 12.2, 323), 'test3940.6False')
self.assertRaises(InputTypeError, lambda:
py3.func_defaults_typecheck2('test', 12.2, 323, 3.5))
self.assertRaises(InputTypeError, lambda:
py3.func_defaults_typecheck('qvw', 'abc', 3.5, 4.1))
self.assertRaises(InputTypeError, lambda: py3.func_defaults_typecheck(7, 'qvw'))
self.assertEqual(py3.func_defaults_checkargs('qvw', 'abc', 3, 1.5), 'qvwabcabcabc')
self.assertRaises(InputTypeError, lambda:
py3.func_defaults_checkargs('qvw', 'abc', 3.5))
self.assertRaises(InputTypeError, lambda:
py3.func_defaults_checkargs('qvw', 'abc', 3.5, 4.1))
self.assertRaises(InputTypeError, lambda: py3.func_defaults_checkargs(7, 'qvw'))
self.assertEqual(get_types(py3.func_defaults_annotations),
(Tuple[str, Any, int], str))
self.assertEqual(pytypes.get_type_hints(py3.func_defaults_annotations),
{'a': str, 'c': int, 'return': str})
self.assertEqual(py3.func_defaults_annotations.__annotations__,
{'a': str, 'return': str})
pytypes.infer_default_value_types = False
self.assertEqual(get_types(py3.func_defaults_typecheck),
(Tuple[str, Any, Any, Any], str))
self.assertEqual(pytypes.get_type_hints(py3.func_defaults_typecheck),
{'a': str, 'return': str})
self.assertEqual(py3.func_defaults_typecheck('qvw', 'abc', 3.5), 'invalid')
self.assertEqual(py3.func_defaults_typecheck('qvw', 'abc', 3.5, 4.1), 'invalid')
self.assertRaises(InputTypeError, lambda: py3.func_defaults_typecheck(7, 'qvw'))
self.assertEqual(py3.func_defaults_checkargs('qvw', 'abc', 3, 1.5), 'qvwabcabcabc')
self.assertEqual(py3.func_defaults_checkargs('qvw', 'abc', 3.5), 'invalid')
self.assertEqual(py3.func_defaults_checkargs('qvw', 'abc', 3.5, 4.1), 'invalid')
self.assertRaises(InputTypeError, lambda: py3.func_defaults_checkargs(7, 'qvw'))
self.assertEqual(get_types(py3.func_defaults_annotations),
(Tuple[str, Any, Any], str))
self.assertEqual(pytypes.get_type_hints(py3.func_defaults_annotations),
{'a': str, 'return': str})
self.assertEqual(py3.func_defaults_annotations.__annotations__,
{'a': str, 'return': str})
pytypes.infer_default_value_types = tmp
def __call__(self, function):
if not self.shouldCheck:
return function
type_hints = typing.get_type_hints(function)
def precheck(*args, **kwargs):
all_args = kwargs.copy()
all_args.update(dict(zip(function.__code__.co_varnames, args)))
runtime_args = ((n, type(v)) for n, v in all_args.items())
for arg_name, arg_type in runtime_args:
if arg_name not in type_hints:
continue
if not self.is_subtype(arg_type, type_hints[arg_name]):
raise TypeError('In {} type of {} is {} and not {}'.
format(function.__qualname__,
arg_name,
arg_type,
type_hints[arg_name]))
def postcheck(result):
if 'return' in type_hints:
if not self.is_subtype(type(result), type_hints['return']):
raise TypeError('Type of result is {} and not {}'.
format(type(result), type_hints['return']))
return result
if inspect.iscoroutinefunction(function):
async def type_checker(*args, **kwargs):
precheck(*args, **kwargs)
result = await function(*args, **kwargs)
return postcheck(result)
else:
def type_checker(*args, **kwargs):
precheck(*args, **kwargs)
result = function(*args, **kwargs)
return postcheck(result)
return type_checker
def __call__(self, function):
if not self.shouldCheck:
return function
type_hints = typing.get_type_hints(function)
def precheck(*args, **kwargs):
all_args = kwargs.copy()
all_args.update(dict(zip(function.__code__.co_varnames, args)))
runtime_args = ((n, type(v)) for n, v in all_args.items())
for arg_name, arg_type in runtime_args:
if arg_name not in type_hints:
continue
if not self.is_subtype(arg_type, type_hints[arg_name]):
raise TypeError('In {} type of {} is {} and not {}'.
format(function.__qualname__,
arg_name,
arg_type,
type_hints[arg_name]))
def postcheck(result):
if 'return' in type_hints:
if not self.is_subtype(type(result), type_hints['return']):
raise TypeError('Type of result is {} and not {}'.
format(type(result), type_hints['return']))
return result
if inspect.iscoroutinefunction(function):
async def type_checker(*args, **kwargs):
precheck(*args, **kwargs)
result = await function(*args, **kwargs)
return postcheck(result)
else:
def type_checker(*args, **kwargs):
precheck(*args, **kwargs)
result = function(*args, **kwargs)
return postcheck(result)
return type_checker