diff --git a/zipline/pipeline/term.py b/zipline/pipeline/term.py index 31301053..6e647e08 100644 --- a/zipline/pipeline/term.py +++ b/zipline/pipeline/term.py @@ -15,29 +15,13 @@ from zipline.errors import ( WindowLengthNotSpecified, ) from zipline.utils.memoize import lazyval +from zipline.utils.sentinel import sentinel -@object.__new__ # bind a single instance to the name 'NotSpecified' -class NotSpecified(object): - """ - Singleton sentinel value used for Term defaults. - """ - __slots__ = ('__weakref__',) - - def __new__(cls): - raise TypeError("Can't construct new instances of NotSpecified") - - def __repr__(self): - return type(self).__name__ - - def __reduce__(self): - return type(self).__name__ - - def __deepcopy__(self, _memo): - return self - - def __copy__(self): - return self +NotSpecified = sentinel( + 'NotSpecified', + 'Singleton sentinel value used for Term defaults.', +) class Term(with_metaclass(ABCMeta, object)): diff --git a/zipline/utils/sentinel.py b/zipline/utils/sentinel.py new file mode 100644 index 00000000..02e9c6e2 --- /dev/null +++ b/zipline/utils/sentinel.py @@ -0,0 +1,40 @@ +""" +Construction of sentinel objects. + +Sentinel objects are used when you only care to check for object identity. +""" +import sys + + +def sentinel(name, doc=None): + @object.__new__ # bind a single instance to the name 'NotSpecified' + class result(object): + __doc__ = doc + __slots__ = ('__weakref__',) + + def __new__(cls): + raise TypeError("Can't construct new instances of %s" % name) + + def __repr__(self): + return name + + def __reduce__(self): + return name + + def __deepcopy__(self, _memo): + return self + + def __copy__(self): + return self + + cls = type(result) + cls.__name__ = name + try: + # traverse up one frame to find the module where this is defined + cls.__module__ = sys._getframe(1).f_globals.get( + '__name__', + '__main__', + ) + except (AttributeError, ValueError): + pass + return result