Merge pull request #1289 from quantopian/wildcard

wildcard object and doctests
This commit is contained in:
Richard Frank
2016-06-22 18:09:57 -04:00
committed by GitHub
22 changed files with 139 additions and 146 deletions
+1 -1
View File
@@ -43,7 +43,7 @@ before_script:
- pip freeze | sort
script:
- flake8 zipline tests
- nosetests --with-coverage tests/
- nosetests --with-coverage
# deactive env to get access to anaconda command
- source deactivate
- if [[ "$TRAVIS_SECURE_ENV_VARS" = "true" && "$TRAVIS_BRANCH" = "master" && "$TRAVIS_PULL_REQUEST" = "false" ]]; then DO_UPLOAD="true"; else DO_UPLOAD="false"; fi
+1 -1
View File
@@ -105,7 +105,7 @@ install:
- pip freeze | sort
test_script:
- nosetests tests/
- nosetests
- flake8 zipline tests
branches:
+2
View File
@@ -5,6 +5,8 @@ with-ignore-docstrings=1
with-timer=1
timer-top-n=15
cover-package=zipline
with-doctest=1
testmatch=(?:^|[\\b_\\.-])[Tt]est(?!ing)
[metadata]
description-file = README.rst
+1 -1
View File
@@ -125,7 +125,7 @@ def to_dict(l):
Example
-------
>>> to_dict([2, 3, 4])
>>> to_dict([2, 3, 4]) # doctest: +SKIP
{'0': 2, '1': 3, '2': 4}
"""
return dict(zip(map(str, range(len(l))), l))
@@ -1,3 +1,4 @@
#!/usr/bin/env python
#
# Copyright 2013 Quantopian, Inc.
#
+4 -4
View File
@@ -3046,7 +3046,7 @@ class TestAccountControls(WithDataPortal, WithSimParams, ZiplineTestCase):
# format(i, actual_position, expected_positions[i]))
class TestFutureFlip(WithSimParams, WithDataPortal, ZiplineTestCase):
class TestFutureFlip(WithDataPortal, WithSimParams, ZiplineTestCase):
START_DATE = pd.Timestamp('2006-01-09', tz='utc')
END_DATE = pd.Timestamp('2006-01-10', tz='utc')
sid, = ASSET_FINDER_EQUITY_SIDS = (1,)
@@ -3057,8 +3057,8 @@ class TestFutureFlip(WithSimParams, WithDataPortal, ZiplineTestCase):
{
cls.sid: factory.create_trade_history(
cls.sid,
[1, 2, 4],
[1e9, 1e9, 1e9],
[1, 2],
[1e9, 1e9],
timedelta(days=1),
cls.sim_params,
cls.trading_schedule,
@@ -3067,7 +3067,7 @@ class TestFutureFlip(WithSimParams, WithDataPortal, ZiplineTestCase):
index=cls.sim_params.trading_days,
)
@skip
@skip('broken in zipline 1.0.0')
def test_flip_algo(self):
metadata = {1: {'symbol': 'TEST',
'start_date': self.sim_params.trading_days[0],
-91
View File
@@ -1,91 +0,0 @@
from __future__ import print_function
import sys
import doctest
from unittest import TestCase
from zipline import testing
from zipline.lib import adjustment, normalize
from zipline.pipeline import (
engine,
expression,
)
from zipline.utils import (
cache,
data,
functional,
input_validation,
memoize,
numpy_utils,
preprocess,
)
class DoctestTestCase(TestCase):
@classmethod
def setUpClass(cls):
import pdb
# Workaround for the issue addressed by this (unmerged) PR to pdbpp:
# https://bitbucket.org/antocuni/pdb/pull-request/40/fix-ensure_file_can_write_unicode/diff # noqa
if '_pdbpp_path_hack' in pdb.__file__:
cls._skip = True
else:
cls._skip = False
cls.flags = doctest.REPORT_CDIFF | doctest.IGNORE_EXCEPTION_DETAIL
def _check_docs(self, module):
if self._skip:
# Printing this directly to __stdout__ so that it doesn't get
# captured by nose.
print("Warning: Skipping doctests for %s because "
"pdbpp is installed." % module.__name__, file=sys.__stdout__)
return
try:
doctest.testmod(
module,
verbose=True,
raise_on_error=True,
optionflags=self.flags,
)
except doctest.UnexpectedException as e:
raise e.exc_info[1]
except doctest.DocTestFailure as e:
print("Got:")
print(e.got)
raise
def test_adjustment_docs(self):
self._check_docs(adjustment)
def test_expression_docs(self):
self._check_docs(expression)
def test_engine_docs(self):
self._check_docs(engine)
def test_memoize_docs(self):
self._check_docs(memoize)
def test_testing_docs(self):
self._check_docs(testing)
def test_preprocess_docs(self):
self._check_docs(preprocess)
def test_input_validation_docs(self):
self._check_docs(input_validation)
def test_cache_docs(self):
self._check_docs(cache)
def test_numpy_utils_docs(self):
self._check_docs(numpy_utils)
def test_data_docs(self):
self._check_docs(data)
def test_functional_docs(self):
self._check_docs(functional)
def test_normalize_docs(self):
self._check_docs(normalize)
+1 -1
View File
@@ -122,7 +122,7 @@ def simplex_projection(v, b=1):
:Example:
>>> proj = simplex_projection([.4 ,.3, -.4, .5])
>>> print(proj)
>>> proj # doctest: +NORMALIZE_WHITESPACE
array([ 0.33333333, 0.23333333, 0. , 0.43333333])
>>> print(proj.sum())
1.0
+1 -1
View File
@@ -226,7 +226,7 @@ class LabelArray(ndarray):
1. Someone tries to directly construct a new array by doing::
>>> ndarray.__new__(LabelArray, ...)
>>> ndarray.__new__(LabelArray, ...) # doctest: +SKIP
In this case, obj will be None. We treat this as an error case and
fail.
+11 -7
View File
@@ -333,9 +333,9 @@ class Factor(RestrictedDTypeMixin, ComputableTerm):
Factors. For example, constructing a Factor that computes the average of
two other Factors is simply::
>>> f1 = SomeFactor(...)
>>> f2 = SomeOtherFactor(...)
>>> average = (f1 + f2) / 2.0
>>> f1 = SomeFactor(...) # doctest: +SKIP
>>> f2 = SomeOtherFactor(...) # doctest: +SKIP
>>> average = (f1 + f2) / 2.0 # doctest: +SKIP
Factors can also be converted into :class:`zipline.pipeline.Filter` objects
via comparison operators: (``<``, ``<=``, ``!=``, ``eq``, ``>``, ``>=``).
@@ -492,8 +492,10 @@ class Factor(RestrictedDTypeMixin, ComputableTerm):
to use the ``mask`` parameter to discard values at the extremes of the
distribution::
>>> base = MyFactor(...)
>>> normalized = base.demean(mask=base.percentile_between(1, 99))
>>> base = MyFactor(...) # doctest: +SKIP
>>> normalized = base.demean(
... mask=base.percentile_between(1, 99),
... ) # doctest: +SKIP
``demean()`` is only supported on Factors of dtype float64.
@@ -553,8 +555,10 @@ class Factor(RestrictedDTypeMixin, ComputableTerm):
outliers, it is often useful to use the ``mask`` parameter to discard
values at the extremes of the distribution::
>>> base = MyFactor(...)
>>> normalized = base.zscore(mask=base.percentile_between(1, 99))
>>> base = MyFactor(...) # doctest: +SKIP
>>> normalized = base.zscore(
... mask=base.percentile_between(1, 99),
... ) # doctest: +SKIP
``zscore()`` is only supported on Factors of dtype float64.
+4 -4
View File
@@ -25,9 +25,9 @@ def delimit(delimiters, content):
"""
Surround `content` with the first and last characters of `delimiters`.
>>> delimit('[]', "foo")
[foo]
>>> delimit('""', "foo")
>>> delimit('[]', "foo") # doctest: +SKIP
'[foo]'
>>> delimit('""', "foo") # doctest: +SKIP
'"foo"'
"""
if len(delimiters) != 2:
@@ -220,7 +220,7 @@ def format_attrs(attrs):
Example
-------
>>> format_attrs({'key1': 'value1', 'key2': 'value2'})
>>> format_attrs({'key1': 'value1', 'key2': 'value2'}) # doctest: +SKIP
'[key1=value1, key2=value2]'
"""
if not attrs:
+27 -1
View File
@@ -12,6 +12,7 @@ import operator
import os
from os.path import abspath, dirname, join, realpath
import shutil
from sys import _getframe
import tempfile
from logbook import TestHandler
@@ -23,7 +24,7 @@ from six import itervalues, iteritems, with_metaclass
from six.moves import filter, map
from sqlalchemy import create_engine
from testfixtures import TempDirectory
from toolz import concat
from toolz import concat, curry
from zipline.assets import AssetFinder, AssetDBWriter
from zipline.assets.synthetic import make_simple_equity_info
@@ -860,6 +861,7 @@ class SubTestFailures(AssertionError):
)
@nottest
def subtest(iterator, *_names):
"""
Construct a subtest in a unittest.
@@ -1374,3 +1376,27 @@ def patch_read_csv(url_map, module=pd, strict=False):
with patch.object(module, 'read_csv', patched_read_csv):
yield
@curry
def ensure_doctest(f, name=None):
"""Ensure that an object gets doctested. This is useful for instances
of objects like curry or partial which are not discovered by default.
Parameters
----------
f : any
The thing to doctest.
name : str, optional
The name to use in the doctest function mapping. If this is None,
Then ``f.__name__`` will be used.
Returns
-------
f : any
``f`` unchanged.
"""
_getframe(2).f_globals.setdefault('__test__', {})[
f.__name__ if name is None else name
] = f
return f
+1 -1
View File
@@ -210,7 +210,7 @@ def alias(attr_name):
>>> class C(object):
... attr = 1
...
>>> class D(object):
>>> class D(C):
... attr_alias = alias('attr')
...
>>> D.attr
+36 -1
View File
@@ -39,12 +39,47 @@ from six import iteritems, viewkeys, PY2
from toolz import dissoc, keyfilter
import toolz.curried.operator as op
from zipline.testing.core import ensure_doctest
from zipline.dispatch import dispatch
from zipline.lib.adjustment import Adjustment
from zipline.utils.functional import dzip_exact
from zipline.utils.functional import dzip_exact, instance
from zipline.utils.math_utils import tolerant_equals
@instance
@ensure_doctest
class wildcard(object):
"""An object that compares equal to any other object.
This is useful when using :func:`~zipline.testing.predicates.assert_equal`
with a large recursive structure and some fields to be ignored.
Examples
--------
>>> wildcard == 5
True
>>> wildcard == 'ayy'
True
# reflected
>>> 5 == wildcard
True
>>> 'ayy' == wildcard
True
"""
@staticmethod
def __eq__(other):
return True
@staticmethod
def __ne__(other):
return False
def __repr__(self):
return '<%s>' % type(self).__name__
__str__ = __repr__
def keywords(func):
"""Get the argument names of a function
+1
View File
@@ -41,6 +41,7 @@ class CachedObject(namedtuple("_CachedObject", "value expires")):
>>> obj.unwrap(expires)
1
>>> obj.unwrap(expires + Timedelta('1 minute'))
... # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
Expired: 2014-01-01 00:00:00+00:00
+1 -1
View File
@@ -41,7 +41,7 @@ class CallbackManager(object):
>>> with manager('example'):
... print('inside example block')
entering example block
inside example
inside example block
exiting example block
These are reusable with different args:
+1 -1
View File
@@ -23,7 +23,7 @@ def invert(d):
"""
Invert a dictionary into a dictionary of sets.
>>> invert({'a': 1, 'b': 2, 'c': 1})
>>> invert({'a': 1, 'b': 2, 'c': 1}) # doctest: +SKIP
{1: {'a', 'c'}, 2: {'b'}}
"""
out = {}
+4 -2
View File
@@ -302,7 +302,8 @@ class AfterOpen(StatelessRule):
A rule that triggers for some offset after the market opens.
Example that triggers after 30 minutes of the market opening:
>>> AfterOpen(minutes=30)
>>> AfterOpen(minutes=30) # doctest: +ELLIPSIS
<zipline.utils.events.AfterOpen object at ...>
"""
def __init__(self, offset=None, **kwargs):
self.offset = _build_offset(
@@ -346,7 +347,8 @@ class BeforeClose(StatelessRule):
A rule that triggers for some offset time before the market closes.
Example that triggers for the last 30 minutes every day:
>>> BeforeClose(minutes=30)
>>> BeforeClose(minutes=30) # doctest: +ELLIPSIS
<zipline.utils.events.BeforeClose object at ...>
"""
def __init__(self, offset=None, **kwargs):
self.offset = _build_offset(
+1 -1
View File
@@ -69,7 +69,7 @@ class final(with_metaclass(ABCMeta)):
Example usage:
>>> from six import with_metaclass
>>> class C(with_metaclass(FinalMeta)):
>>> class C(with_metaclass(FinalMeta, object)):
... @final
... def f(self):
... return 'value'
+3 -1
View File
@@ -220,14 +220,16 @@ def unzip(seq, elem_len=None):
>>> seq = [('a', 1), ('b', 2), ('c', 3, 'extra')]
>>> cs, ns = unzip(seq)
Traceback (most recent call last):
...
...
ValueError: element at index 2 was length 3, expected 2
# allows an explicit element length instead of infering
>>> seq = [('a', 1, 'extra'), ('b', 2), ('c', 3)]
>>> cs, ns = unzip(seq, 2)
Traceback (most recent call last):
...
ValueError: element at index 0 was length 3, expected 2
# handles empty sequences when a length is given
>>> cs, ns = unzip([], elem_len=2)
>>> cs == ns == ()
+22 -15
View File
@@ -209,17 +209,19 @@ def expect_dtypes(**named):
Usage
-----
>>> from numpy import dtype, arange
>>> @expect_dtypes(x=dtype(int))
>>> from numpy import dtype, arange, int8, float64
>>> @expect_dtypes(x=dtype(int8))
... def foo(x, y):
... return x, y
...
>>> foo(arange(3), 'foo')
(array([0, 1, 2]), 'foo')
>>> foo(arange(3, dtype=float), 'foo')
>>> foo(arange(3, dtype=int8), 'foo')
(array([0, 1, 2], dtype=int8), 'foo')
>>> foo(arange(3, dtype=float64), 'foo') # doctest: +NORMALIZE_WHITESPACE
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
TypeError: foo() expected an argument with dtype 'int64' for argument 'x', but got dtype 'float64' instead. # noqa
TypeError: ...foo() expected a value with dtype 'int8' for argument 'x',
but got 'float64' instead.
"""
for name, type_ in iteritems(named):
if not isinstance(type_, (dtype, tuple)):
@@ -278,10 +280,11 @@ def expect_kinds(**named):
2
>>> foo(int32(2))
2
>>> foo(float32(2))
>>> foo(float32(2)) # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...n
TypeError: foo() expected a numpy object of kind 'i' for argument 'x', but got 'f' instead. # noqa
...
TypeError: ...foo() expected a numpy object of kind 'i' for argument 'x',
but got 'f' instead.
"""
for name, kind in iteritems(named):
if not isinstance(kind, (str, tuple)):
@@ -337,10 +340,11 @@ def expect_types(*_pos, **named):
...
>>> foo(2, '3')
(2, '3')
>>> foo(2.0, '3')
>>> foo(2.0, '3') # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
TypeError: foo() expected an argument of type 'int' for argument 'x', but got float instead. # noqa
TypeError: ...foo() expected a value of type int for argument 'x',
but got float instead.
"""
if _pos:
raise TypeError("expect_types() only takes keyword arguments.")
@@ -463,10 +467,11 @@ def expect_element(*_pos, **named):
'A'
>>> foo('b')
'B'
>>> foo('c')
>>> foo('c') # doctest: +NORMALIZE_WHITESPACE +ELLIPSIS
Traceback (most recent call last):
...
ValueError: foo() expected a value in ('a', 'b') for argument 'x', but got 'c' instead. # noqa
ValueError: ...foo() expected a value in ('a', 'b') for argument 'x',
but got 'c' instead.
Notes
-----
@@ -505,10 +510,12 @@ def expect_dimensions(**dimensions):
...
>>> foo(array([1, 1]), array([[1, 1], [2, 2]]))
2
>>> foo(array([1, 1], array([1, 1])))
>>> foo(array([1, 1]), array([1, 1])) # doctest: +NORMALIZE_WHITESPACE
... # doctest: +ELLIPSIS
Traceback (most recent call last):
...
TypeError: foo() expected a 2-D array for argument 'y', but got a 1-D array instead. # noqa
ValueError: ...foo() expected a 2-D array for argument 'y',
but got a 1-D array instead.
"""
def _expect_dimension(expected_ndim):
def _check(func, argname, argvalue):
+15 -11
View File
@@ -24,14 +24,15 @@ def compose_types(a, *cs):
.. code-block:: python
class M(type):
def __new__(mcls, name, bases, dict_):
dict_['ayy'] = 'lmao'
return super().__new__(mcls, name, bases, dict_)
>>> class M(type):
... def __new__(mcls, name, bases, dict_):
... dict_['ayy'] = 'lmao'
... return super(M, mcls).__new__(mcls, name, bases, dict_)
class C(metaclass=M):
pass
>>> from six import with_metaclass
>>> class C(with_metaclass(M, object)):
... pass
We now want to create a sublclass of ``C`` that is also an abstract class.
@@ -43,10 +44,11 @@ def compose_types(a, *cs):
.. code-block:: python
class D(C, metaclass=compose_types(M, ABCMeta)):
@abstractmethod
def f(self):
raise NotImplementedError('f')
>>> from abc import ABCMeta, abstractmethod
>>> class D(with_metaclass(compose_types(M, ABCMeta), C)):
... @abstractmethod
... def f(self):
... raise NotImplementedError('f')
We can see that this class has both metaclasses applied to it:
@@ -54,8 +56,10 @@ def compose_types(a, *cs):
.. code-block:: python
>>> D.ayy
lmao
'lmao'
>>> D()
Traceback (most recent call last):
...
TypeError: Can't instantiate abstract class D with abstract methods f