Completed
Pull Request — master (#937)
by
unknown
01:21
created

_conda_format()   A

Complexity

Conditions 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
dl 0
loc 5
rs 9.4286
1
#!/usr/bin/env python
2
#
3
# Copyright 2014 Quantopian, Inc.
4
#
5
# Licensed under the Apache License, Version 2.0 (the "License");
6
# you may not use this file except in compliance with the License.
7
# You may obtain a copy of the License at
8
#
9
#     http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing, software
12
# distributed under the License is distributed on an "AS IS" BASIS,
13
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
# See the License for the specific language governing permissions and
15
# limitations under the License.
16
from __future__ import print_function
17
import os
18
import re
19
import sys
20
from operator import lt, gt, eq, le, ge
21
from os.path import (
22
    abspath,
23
    dirname,
24
    join,
25
)
26
from distutils.version import StrictVersion
27
from setuptools import (
28
    Extension,
29
    find_packages,
30
    setup,
31
)
32
33
import versioneer
34
35
36
class LazyCythonizingList(list):
37
    cythonized = False
38
39
    def lazy_cythonize(self):
40
        if self.cythonized:
41
            return
42
        self.cythonized = True
43
44
        from Cython.Build import cythonize
45
        from numpy import get_include
46
47
        self[:] = cythonize(
48
            [
49
                Extension(*ext_args, include_dirs=[get_include()])
50
                for ext_args in self
51
            ]
52
        )
53
54
    def __iter__(self):
55
        self.lazy_cythonize()
56
        return super(LazyCythonizingList, self).__iter__()
57
58
    def __getitem__(self, num):
59
        self.lazy_cythonize()
60
        return super(LazyCythonizingList, self).__getitem__(num)
61
62
63
ext_modules = LazyCythonizingList([
64
    ('zipline.assets._assets', ['zipline/assets/_assets.pyx']),
65
    ('zipline.lib.adjustment', ['zipline/lib/adjustment.pyx']),
66
    ('zipline.lib._float64window', ['zipline/lib/_float64window.pyx']),
67
    ('zipline.lib._int64window', ['zipline/lib/_int64window.pyx']),
68
    ('zipline.lib._uint8window', ['zipline/lib/_uint8window.pyx']),
69
    ('zipline.lib.rank', ['zipline/lib/rank.pyx']),
70
    ('zipline.data._equities', ['zipline/data/_equities.pyx']),
71
    ('zipline.data._adjustments', ['zipline/data/_adjustments.pyx']),
72
])
73
74
75
STR_TO_CMP = {
76
    '<': lt,
77
    '<=': le,
78
    '=': eq,
79
    '==': eq,
80
    '>': gt,
81
    '>=': ge,
82
}
83
84
85
def _filter_requirements(lines_iter):
86
    for line in lines_iter:
87
        line = line.strip()
88
        if not line or line.startswith('#'):
89
            continue
90
91
        # pip install -r understands line with ;python_version<'3.0', but
92
        # whatever happens inside extras_requires doesn't.  Parse the line
93
        # manually and conditionally add it if needed.
94
        if ';' not in line:
95
            yield line
96
            continue
97
98
        requirement, version_spec = line.split(';')
99
        try:
100
            groups = re.match(
101
                "(python_version)([<>=]{1,2})(')([0-9\.]+)(')(.*)",
102
                version_spec,
103
            ).groups()
104
            comp = STR_TO_CMP[groups[1]]
105
            version_spec = StrictVersion(groups[3])
106
        except Exception as e:
107
            # My kingdom for a 'raise from'!
108
            raise AssertionError(
109
                "Couldn't parse requirement line; '%s'\n"
110
                "Error was:\n"
111
                "%r" % (line, e)
112
            )
113
114
        sys_version = '.'.join(list(map(str, sys.version_info[:3])))
115
        if comp(sys_version, version_spec):
116
            yield requirement
117
118
119
REQ_UPPER_BOUNDS = {
120
    'numpy': '<1.10',
121
}
122
123
124
def _with_bounds(req):
125
    try:
126
        req, lower = req.split('==')
127
    except ValueError:
128
        return req
129
    else:
130
        with_bounds = [req, '>=', lower]
131
        upper = REQ_UPPER_BOUNDS.get(req)
132
        if upper:
133
            with_bounds.extend([',', upper])
134
        return ''.join(with_bounds)
135
136
137
REQ_PATTERN = re.compile("([^=<>]+)([<=>]{1,2})(.*)")
138
139
140
def _conda_format(req):
141
    return REQ_PATTERN.sub(
142
        lambda m: '%s %s%s' % (m.group(1).lower(), m.group(2), m.group(3)),
143
        req,
144
        1,
145
    )
146
147
148
def read_requirements(path, strict_bounds, conda_format=False):
149
    """
150
    Read a requirements.txt file, expressed as a path relative to Zipline root.
151
152
    Returns requirements with the pinned versions as lower bounds
153
    if `strict_bounds` is falsey.
154
    """
155
    real_path = join(dirname(abspath(__file__)), path)
156
    with open(real_path) as f:
157
        reqs = _filter_requirements(f.readlines())
158
159
        if not strict_bounds:
160
            reqs = map(_with_bounds, reqs)
161
162
        if conda_format:
163
            reqs = map(_conda_format, reqs)
164
165
        return list(reqs)
166
167
168
def install_requires(strict_bounds=False, conda_format=False):
169
    return read_requirements('etc/requirements.txt',
170
                             strict_bounds=strict_bounds,
171
                             conda_format=conda_format)
172
173
174
def extras_requires(conda_format=False):
175
    dev_reqs = read_requirements('etc/requirements_dev.txt',
176
                                 strict_bounds=True,
177
                                 conda_format=conda_format)
178
    talib_reqs = ['TA-Lib==0.4.9']
179
    return {
180
        'dev': dev_reqs,
181
        'talib': talib_reqs,
182
        'all': dev_reqs + talib_reqs,
183
    }
184
185
186
def module_requirements(requirements_path, module_names):
187
    module_names = set(module_names)
188
    found = set()
189
    module_lines = []
190
    for line in read_requirements(requirements_path, strict_bounds=True):
191
        match = REQ_PATTERN.match(line)
192
        if match is None:
193
            raise AssertionError("Could not parse requirement: '%s'" % line)
194
195
        name = match.group(1)
196
        if name in module_names:
197
            found.add(name)
198
            module_lines.append(line)
199
200
    if found != module_names:
201
        raise AssertionError(
202
            "No requirements found for %s." % module_names - found
203
        )
204
    return module_lines
205
206
207
def pre_setup():
208
    if not set(sys.argv) & {'install', 'develop', 'egg_info', 'bdist_wheel'}:
209
        return
210
211
    try:
212
        import pip
213
        if StrictVersion(pip.__version__) < StrictVersion('7.1.0'):
214
            raise AssertionError(
215
                "Zipline installation requires pip>=7.1.0, but your pip "
216
                "version is {version}. \n"
217
                "You can upgrade your pip with "
218
                "'pip install --upgrade pip'.".format(
219
                    version=pip.__version__,
220
                )
221
            )
222
    except ImportError:
223
        raise AssertionError("Zipline installation requires pip")
224
225
    required = ('Cython', 'numpy')
226
    for line in module_requirements('etc/requirements.txt', required):
227
        pip.main(['install', line])
228
229
230
pre_setup()
231
232
conda_build = os.path.basename(sys.argv[0]) == 'conda-build'
233
234
setup(
235
    name='zipline',
236
    version=versioneer.get_version(),
237
    cmdclass=versioneer.get_cmdclass(),
238
    description='A backtester for financial algorithms.',
239
    author='Quantopian Inc.',
240
    author_email='[email protected]',
241
    packages=find_packages('.', include=['zipline', 'zipline.*']),
242
    ext_modules=ext_modules,
243
    scripts=['scripts/run_algo.py'],
244
    include_package_data=True,
245
    license='Apache 2.0',
246
    classifiers=[
247
        'Development Status :: 4 - Beta',
248
        'License :: OSI Approved :: Apache Software License',
249
        'Natural Language :: English',
250
        'Programming Language :: Python',
251
        'Programming Language :: Python :: 2.7',
252
        'Programming Language :: Python :: 3.3',
253
        'Programming Language :: Python :: 3.4',
254
        'Operating System :: OS Independent',
255
        'Intended Audience :: Science/Research',
256
        'Topic :: Office/Business :: Financial',
257
        'Topic :: Scientific/Engineering :: Information Analysis',
258
        'Topic :: System :: Distributed Computing',
259
    ],
260
    install_requires=install_requires(conda_format=conda_build),
261
    extras_require=extras_requires(conda_format=conda_build),
262
    url="http://zipline.io",
263
)
264