diff --git a/.travis.yml b/.travis.yml index 23df7e87..4d70f7d6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,8 +20,10 @@ install: - source activate testenv - conda install --yes -c https://conda.binstar.org/Quantopian numpy=$NUMPY_VERSION pandas=$PANDAS_VERSION scipy==$SCIPY_VERSION matplotlib Cython patsy statsmodels tornado pyparsing xlrd mock pytz requests six dateutil ta-lib logbook - pip install --upgrade pip coverage - - pip install -e .[dev] + - pip install -r etc/requirements.txt + - pip install -r etc/requirements_dev.txt - pip install -r etc/requirements_blaze.txt + - python setup.py build_ext --inplace before_script: - pip freeze | sort - "flake8 zipline tests" diff --git a/docs/source/whatsnew/0.8.4.txt b/docs/source/whatsnew/0.8.4.txt index fb2a12dd..92d721ce 100644 --- a/docs/source/whatsnew/0.8.4.txt +++ b/docs/source/whatsnew/0.8.4.txt @@ -43,7 +43,7 @@ None Build ~~~~~ -None +* Makes zipline install requirements more flexible (:issue:`825`). Documentation ~~~~~~~~~~~~~ diff --git a/setup.py b/setup.py index 2015fdb3..7c6db032 100644 --- a/setup.py +++ b/setup.py @@ -118,21 +118,48 @@ def _filter_requirements(lines_iter): yield requirement -def read_requirements(path): +REQ_UPPER_BOUNDS = { +} + + +def _with_bounds(req): + try: + req, lower = req.split('==') + except ValueError: + return req + else: + with_bounds = [req, '>=', lower] + upper = REQ_UPPER_BOUNDS.get(req) + if upper: + with_bounds.extend([',', upper]) + return ''.join(with_bounds) + + +def read_requirements(path, strict_bounds): """ Read a requirements.txt file, expressed as a path relative to Zipline root. + + Returns requirements with the pinned versions as lower bounds + if `strict_bounds` is falsey. """ real_path = join(dirname(abspath(__file__)), path) with open(real_path) as f: - return list(_filter_requirements(f.readlines())) + reqs = _filter_requirements(f.readlines()) + + if strict_bounds: + return list(reqs) + else: + return list(map(_with_bounds, reqs)) -def install_requires(): - return read_requirements('etc/requirements.txt') +def install_requires(strict_bounds=False): + return read_requirements('etc/requirements.txt', + strict_bounds=strict_bounds) def extras_requires(): - dev_reqs = read_requirements('etc/requirements_dev.txt') + dev_reqs = read_requirements('etc/requirements_dev.txt', + strict_bounds=True) talib_reqs = ['TA-Lib==0.4.9'] return { 'dev': dev_reqs, @@ -146,7 +173,7 @@ def module_requirements(requirements_path, module_names): found = set() module_lines = [] parser = re.compile("([^=<>]+)([<=>]{1,2})(.*)") - for line in read_requirements(requirements_path): + for line in read_requirements(requirements_path, strict_bounds=False): match = parser.match(line) if match is None: raise AssertionError("Could not parse requirement: '%s'" % line)