mirror of
https://github.com/wassname/catalyst.git
synced 2026-07-10 07:30:28 +08:00
DEV: combined the decorators
This way the type decorator could have access to the argument decorator’s keyword list.
This commit is contained in:
+40
-48
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user