DEV: combined the decorators

This way the type decorator could have access to the argument
decorator’s keyword list.
This commit is contained in:
Jean Bredeche
2016-04-13 16:01:00 -04:00
parent 2775cc7ca4
commit d597a3caaa
+40 -48
View File
@@ -27,75 +27,71 @@ from zipline.assets import Asset
from zipline.zipline_warnings import ZiplineDeprecationWarning
class assert_keywords(object):
cdef bool _is_iterable(obj):
return isinstance(obj, Iterable) and not isinstance(obj, str)
cdef class check_parameters(object):
"""
Asserts that the keywords passed into the wrapped function are included
in those passed into this decorator. If not, raise a TypeError with a
meaningful message, unlike the one cython returns by default.
"""
meaningful message, unlike the one Cython returns by default.
def __init__(self, arg_names, method_name):
self.names = arg_names
Also asserts that the arguments passed into the wrapped function are
consistent with the types passed into this decorator. If not, raise a
TypeError with a meaningful message.
"""
cdef tuple keyword_names
cdef object method
cdef tuple types
cdef dict keys_to_types
def __init__(self, method_name, keyword_names, types):
self.keyword_names = keyword_names
self.types = types
self.method = method_name
self.keys_to_types = dict(zip(keyword_names, types))
def __call__(self, func):
def assert_keywords_and_call(*args, **kwargs):
# verify all the keyword arguments
for field in kwargs:
if field not in self.names:
if field not in self.keyword_names:
raise TypeError("%s() got an unexpected keyword argument"
" '%s'" % (self.method, field))
return func(*args, **kwargs)
return assert_keywords_and_call
KEYWORDS = ['assets', 'fields', 'bar_count', 'frequency']
class assert_types(object):
"""
Asserts that the arguments passed into the wrapped function are consistent
with the types passed into this decorator. If not, raise a TypeError with
a meaningful message.
"""
def __init__(self, *args):
self.types = args
self.keys_to_types = dict(zip(KEYWORDS, args))
def _is_iterable(self, obj):
return isinstance(obj, Iterable) and not isinstance(obj, str)
def __call__(self, func):
def assert_types_and_call(*args, **kwargs):
# verify type of each arg
for i, arg in enumerate(args[1:]):
if isinstance(arg, self.types[i]):
continue
elif i in (0, 1) and self._is_iterable(arg):
elif i in (0, 1) and _is_iterable(arg):
if isinstance(arg[0], self.types[i]):
continue
expected_type = self.types[i].__name__ \
if not self._is_iterable(self.types[i]) \
if not _is_iterable(self.types[i]) \
else ', '.join([type.__name__ for type in self.types[i]])
raise TypeError("Expected %s argument to be of type %s%s" %
(KEYWORDS[i],
(self.keyword_names[i],
'or iterable of type ' if i in (0, 1) else '',
expected_type)
)
# verify type of each kwarg
for keyword, arg in iteritems(kwargs):
if isinstance(arg, self.keys_to_types[keyword]):
continue
elif keyword in ('assets', 'fields') and \
self._is_iterable(arg):
elif keyword in ('assets', 'fields') and _is_iterable(arg):
if isinstance(arg[0], self.keys_to_types[i]):
continue
expected_type = self.keys_to_types[keyword].__name__ \
if not self._is_iterable(self.keys_to_types[keyword]) \
if not _is_iterable(self.keys_to_types[keyword]) \
else ', '.join([type.__name__ for type in
self.keys_to_types[keyword]])
raise TypeError("Expected %s argument to be of type %s%s" %
(keyword,
'or iterable of type ' if keyword in
@@ -105,7 +101,7 @@ class assert_types(object):
return func(*args, **kwargs)
return assert_types_and_call
return assert_keywords_and_call
@contextmanager
@@ -210,8 +206,7 @@ cdef class BarData:
return dt
@assert_keywords(arg_names=('assets', 'fields'), method_name='current')
@assert_types((Asset, str), str)
@check_parameters('current', ('assets', 'fields'), ((Asset, str), str))
def current(self, assets, fields):
"""
Returns the current value of the given assets for the given fields
@@ -268,8 +263,8 @@ cdef class BarData:
the current trade bar. If there is no current trade bar, NaN is
returned.
"""
multiple_assets = self._is_iterable(assets)
multiple_fields = self._is_iterable(fields)
multiple_assets = _is_iterable(assets)
multiple_fields = _is_iterable(fields)
# There's some overly verbose code in here, particularly around
# 'do something if self._adjust_minutes is False, otherwise do
@@ -386,10 +381,7 @@ cdef class BarData:
return pd.DataFrame(data)
cdef bool _is_iterable(self, obj):
return hasattr(obj, '__iter__') and not isinstance(obj, str)
@assert_types(Asset)
@check_parameters('can_trade', ('assets',), (Asset,))
def can_trade(self, assets):
"""
For the given asset or iterable of assets, returns true if the asset
@@ -436,7 +428,7 @@ cdef class BarData:
return False
@assert_types(Asset)
@check_parameters('is_stale', ('assets',), (Asset,))
def is_stale(self, assets):
"""
For the given asset or iterable of assets, returns true if the asset
@@ -496,9 +488,9 @@ cdef class BarData:
return not (last_traded_dt is pd.NaT)
@assert_keywords(arg_names=('assets', 'fields', 'bar_count', 'frequency'),
method_name='history')
@assert_types((Asset, str), str, int, str)
@check_parameters('history',
('assets', 'fields', 'bar_count', 'frequency'),
((Asset, str), str, int, str))
def history(self, assets, fields, bar_count, frequency):
"""
Returns a window of data for the given assets and fields.