mirror of
https://github.com/wassname/catalyst.git
synced 2026-09-09 11:19:23 +08:00
ENH: Coerce user input with API method decorator
Previously we have capitalized input strings at different levels in our code: in the user-facing API methods and in the asset finder. This commit moves input string capitalization exclusively to the API method to which the string was supplied. Specifically, the string is capitalized by a preprocess API method decorator. The preprocess decorator passes the input string to the newly defined ensure_upper_case() method, which returns a TypeError if the argument supplied is not a string. ensure_upper_case() is defined in a new file, zipline/utils/input_validation.py. The existing expect_types() method is also moved there. Various tests in tests/test_assets.py are modified to account for the fact that the asset finder method lookup_symol() no longer capitalizes its supplied argument.
This commit is contained in:
@@ -6,45 +6,12 @@ from functools import wraps
|
||||
from inspect import getargspec
|
||||
from uuid import uuid4
|
||||
|
||||
from six import iteritems, viewkeys, exec_
|
||||
from toolz import valmap
|
||||
from six import viewkeys, exec_
|
||||
|
||||
|
||||
NO_DEFAULT = object()
|
||||
|
||||
|
||||
def expect_types(*_pos, **named):
|
||||
"""
|
||||
Preprocessing decorator that verifies inputs have expected types.
|
||||
|
||||
Usage
|
||||
-----
|
||||
>>> @expect_types(x=int, y=str)
|
||||
... def foo(x, y):
|
||||
... return x, y
|
||||
...
|
||||
>>> foo(2, '3')
|
||||
(2, '3')
|
||||
>>> foo(2.0, '3')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
TypeError: foo() expected an argument of type 'int' for argument 'x', but got float instead. # noqa
|
||||
"""
|
||||
if _pos:
|
||||
raise TypeError("expect_types() only takes keyword arguments.")
|
||||
|
||||
for name, type_ in iteritems(named):
|
||||
if not isinstance(type_, (type, tuple)):
|
||||
raise TypeError(
|
||||
"expect_types() expected a type or tuple of types for "
|
||||
"argument '{name}', but got {type_} instead.".format(
|
||||
name=name, type_=type_,
|
||||
)
|
||||
)
|
||||
|
||||
return preprocess(**valmap(_expect_type, named))
|
||||
|
||||
|
||||
def preprocess(*_unused, **processors):
|
||||
"""
|
||||
Decorator that applies pre-processors to the arguments of a function before
|
||||
@@ -157,69 +124,6 @@ def call(f):
|
||||
return processor
|
||||
|
||||
|
||||
def _qualified_name(obj):
|
||||
"""
|
||||
Return the fully-qualified name (ignoring inner classes) of a type.
|
||||
"""
|
||||
module = obj.__module__
|
||||
if module in ('__builtin__', '__main__', 'builtins'):
|
||||
return obj.__name__
|
||||
return '.'.join([module, obj.__name__])
|
||||
|
||||
|
||||
def _expect_type(type_):
|
||||
"""
|
||||
Factory for type-checking functions that work the @preprocess decorator.
|
||||
"""
|
||||
# Slightly different messages for type and tuple of types.
|
||||
_template = (
|
||||
"{{funcname}}() expected a value of type {type_or_types} "
|
||||
"for argument '{{argname}}', but got {{actual}} instead."
|
||||
)
|
||||
if isinstance(type_, tuple):
|
||||
template = _template.format(
|
||||
type_or_types=' or '.join(map(_qualified_name, type_))
|
||||
)
|
||||
else:
|
||||
template = _template.format(type_or_types=_qualified_name(type_))
|
||||
|
||||
def _check_type(func, argname, argvalue):
|
||||
if not isinstance(argvalue, type_):
|
||||
raise TypeError(
|
||||
template.format(
|
||||
funcname=_qualified_name(func),
|
||||
argname=argname,
|
||||
actual=_qualified_name(type(argvalue)),
|
||||
)
|
||||
)
|
||||
return argvalue
|
||||
return _check_type
|
||||
|
||||
|
||||
def optional(type_):
|
||||
"""
|
||||
Helper for use with `expect_types` when an input can be `type_` or `None`.
|
||||
|
||||
Returns an object such that both `None` and instances of `type_` pass
|
||||
checks of the form `isinstance(obj, optional(type_))`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
type_ : type
|
||||
Type for which to produce an option.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> isinstance({}, optional(dict))
|
||||
True
|
||||
>>> isinstance(None, optional(dict))
|
||||
True
|
||||
>>> isinstance(1, optional(dict))
|
||||
False
|
||||
"""
|
||||
return (type_, type(None))
|
||||
|
||||
|
||||
def _build_preprocessed_function(func, processors, args_defaults):
|
||||
"""
|
||||
Build a preprocessed function with the same signature as `func`.
|
||||
|
||||
Reference in New Issue
Block a user