diff --git a/.travis.yml b/.travis.yml index 0d71e987..dcd64715 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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 diff --git a/appveyor.yml b/appveyor.yml index 4b68e521..e0c0df3b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -105,7 +105,7 @@ install: - pip freeze | sort test_script: - - nosetests tests/ + - nosetests - flake8 zipline tests branches: diff --git a/setup.cfg b/setup.cfg index 5e505a5f..7b516d6b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 diff --git a/tests/pipeline/test_term.py b/tests/pipeline/test_term.py index e6db7d1d..ca88776e 100644 --- a/tests/pipeline/test_term.py +++ b/tests/pipeline/test_term.py @@ -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)) diff --git a/tests/risk/upload_answer_key.py b/tests/risk/upload_answer_key similarity index 98% rename from tests/risk/upload_answer_key.py rename to tests/risk/upload_answer_key index 9c4f5323..ca47b24d 100644 --- a/tests/risk/upload_answer_key.py +++ b/tests/risk/upload_answer_key @@ -1,3 +1,4 @@ +#!/usr/bin/env python # # Copyright 2013 Quantopian, Inc. # diff --git a/tests/test_doctests.py b/tests/test_doctests.py deleted file mode 100644 index 0492485b..00000000 --- a/tests/test_doctests.py +++ /dev/null @@ -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) diff --git a/zipline/examples/olmar.py b/zipline/examples/olmar.py index e041c778..ae2caa14 100644 --- a/zipline/examples/olmar.py +++ b/zipline/examples/olmar.py @@ -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 diff --git a/zipline/lib/labelarray.py b/zipline/lib/labelarray.py index a3ef5027..68cf7b4b 100644 --- a/zipline/lib/labelarray.py +++ b/zipline/lib/labelarray.py @@ -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. diff --git a/zipline/pipeline/factors/factor.py b/zipline/pipeline/factors/factor.py index 115cb057..e9484ce9 100644 --- a/zipline/pipeline/factors/factor.py +++ b/zipline/pipeline/factors/factor.py @@ -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. diff --git a/zipline/pipeline/visualize.py b/zipline/pipeline/visualize.py index 9e1c0f95..df416a2a 100644 --- a/zipline/pipeline/visualize.py +++ b/zipline/pipeline/visualize.py @@ -26,9 +26,9 @@ def delimit(delimiters, content): Surround `content` with the first and last characters of `delimiters`. >>> delimit('[]', "foo") - [foo] + u'[foo]' >>> delimit('""', "foo") - '"foo"' + u'"foo"' """ if len(delimiters) != 2: raise ValueError( @@ -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: diff --git a/zipline/testing/fixtures.py b/zipline/testing/fixtures.py index a7851f5f..77342acf 100644 --- a/zipline/testing/fixtures.py +++ b/zipline/testing/fixtures.py @@ -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 diff --git a/zipline/utils/context_tricks.py b/zipline/utils/context_tricks.py index a0a244e6..8eaa64c0 100644 --- a/zipline/utils/context_tricks.py +++ b/zipline/utils/context_tricks.py @@ -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: diff --git a/zipline/utils/control_flow.py b/zipline/utils/control_flow.py index 7f0ab83e..b64d9772 100644 --- a/zipline/utils/control_flow.py +++ b/zipline/utils/control_flow.py @@ -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 = {} diff --git a/zipline/utils/events.py b/zipline/utils/events.py index 1a7bb068..84b6d158 100644 --- a/zipline/utils/events.py +++ b/zipline/utils/events.py @@ -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 + """ 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 + """ def __init__(self, offset=None, **kwargs): self.offset = _build_offset( diff --git a/zipline/utils/final.py b/zipline/utils/final.py index 2e262a9c..453ec00f 100644 --- a/zipline/utils/final.py +++ b/zipline/utils/final.py @@ -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' diff --git a/zipline/utils/functional.py b/zipline/utils/functional.py index 685f0aef..a5282bff 100644 --- a/zipline/utils/functional.py +++ b/zipline/utils/functional.py @@ -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 == () diff --git a/zipline/utils/input_validation.py b/zipline/utils/input_validation.py index 96387b87..7bb96b71 100644 --- a/zipline/utils/input_validation.py +++ b/zipline/utils/input_validation.py @@ -216,10 +216,11 @@ def expect_dtypes(**named): ... >>> foo(arange(3), 'foo') (array([0, 1, 2]), 'foo') - >>> foo(arange(3, dtype=float), 'foo') + >>> foo(arange(3, dtype=float), 'foo') # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... - TypeError: foo() expected an argument with dtype 'int64' for argument 'x', but got dtype 'float64' instead. # noqa + TypeError: zipline.utils.input_validation.foo() expected a value with + dtype 'int64' for argument 'x', but got 'float64' instead. """ for name, type_ in iteritems(named): if not isinstance(type_, (dtype, tuple)): @@ -278,10 +279,11 @@ def expect_kinds(**named): 2 >>> foo(int32(2)) 2 - >>> foo(float32(2)) + >>> foo(float32(2)) # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): - ...n - TypeError: foo() expected a numpy object of kind 'i' for argument 'x', but got 'f' instead. # noqa + ... + TypeError: zipline.utils.input_validation.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 +339,11 @@ def expect_types(*_pos, **named): ... >>> foo(2, '3') (2, '3') - >>> foo(2.0, '3') + >>> foo(2.0, '3') # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... - TypeError: foo() expected an argument of type 'int' for argument 'x', but got float instead. # noqa + TypeError: zipline.utils.input_validation.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 +466,11 @@ def expect_element(*_pos, **named): 'A' >>> foo('b') 'B' - >>> foo('c') + >>> foo('c') # doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... - ValueError: foo() expected a value in ('a', 'b') for argument 'x', but got 'c' instead. # noqa + ValueError: zipline.utils.input_validation.foo() expected a value in + ('a', 'b') for argument 'x', but got 'c' instead. Notes ----- @@ -505,10 +509,11 @@ 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 Traceback (most recent call last): ... - TypeError: foo() expected a 2-D array for argument 'y', but got a 1-D array instead. # noqa + ValueError: zipline.utils.input_validation.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): diff --git a/zipline/utils/metautils.py b/zipline/utils/metautils.py index a2f86080..5f0095bc 100644 --- a/zipline/utils/metautils.py +++ b/zipline/utils/metautils.py @@ -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