Completed
Pull Request — master (#937)
by
unknown
01:17
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
}
121
122
123
def _with_bounds(req):
124
    try:
125
        req, lower = req.split('==')
126
    except ValueError:
127
        return req
128
    else:
129
        with_bounds = [req, '>=', lower]
130
        upper = REQ_UPPER_BOUNDS.get(req)
131
        if upper:
132
            with_bounds.extend([',', upper])
133
        return ''.join(with_bounds)
134
135
136
REQ_PATTERN = re.compile("([^=<>]+)([<=>]{1,2})(.*)")
137
138
139
def _conda_format(req):
140
    return REQ_PATTERN.sub(
141
        lambda m: '%s %s%s' % (m.group(1).lower(), m.group(2), m.group(3)),
142
        req,
143
        1,
144
    )
145
146
147
def read_requirements(path, strict_bounds, conda_format=False):
148
    """
149
    Read a requirements.txt file, expressed as a path relative to Zipline root.
150
151
    Returns requirements with the pinned versions as lower bounds
152
    if `strict_bounds` is falsey.
153
    """
154
    real_path = join(dirname(abspath(__file__)), path)
155
    with open(real_path) as f:
156
        reqs = _filter_requirements(f.readlines())
157
158
        if not strict_bounds:
159
            reqs = map(_with_bounds, reqs)
160
161
        if conda_format:
162
            reqs = map(_conda_format, reqs)
163
164
        return list(reqs)
165
166
167
def install_requires(strict_bounds=False, conda_format=False):
168
    return read_requirements('etc/requirements.txt',
169
                             strict_bounds=strict_bounds,
170
                             conda_format=conda_format)
171
172
173
def extras_requires(conda_format=False):
174
    dev_reqs = read_requirements('etc/requirements_dev.txt',
175
                                 strict_bounds=True,
176
                                 conda_format=conda_format)
177
    talib_reqs = ['TA-Lib==0.4.9']
178
    return {
179
        'dev': dev_reqs,
180
        'talib': talib_reqs,
181
        'all': dev_reqs + talib_reqs,
182
    }
183
184
185
def module_requirements(requirements_path, module_names):
186
    module_names = set(module_names)
187
    found = set()
188
    module_lines = []
189
    for line in read_requirements(requirements_path, strict_bounds=True):
190
        match = REQ_PATTERN.match(line)
191
        if match is None:
192
            raise AssertionError("Could not parse requirement: '%s'" % line)
193
194
        name = match.group(1)
195
        if name in module_names:
196
            found.add(name)
197
            module_lines.append(line)
198
199
    if found != module_names:
200
        raise AssertionError(
201
            "No requirements found for %s." % module_names - found
202
        )
203
    return module_lines
204
205
206
def pre_setup():
207
    if not set(sys.argv) & {'install', 'develop', 'egg_info', 'bdist_wheel'}:
208
        return
209
210
    try:
211
        import pip
212
        if StrictVersion(pip.__version__) < StrictVersion('7.1.0'):
213
            raise AssertionError(
214
                "Zipline installation requires pip>=7.1.0, but your pip "
215
                "version is {version}. \n"
216
                "You can upgrade your pip with "
217
                "'pip install --upgrade pip'.".format(
218
                    version=pip.__version__,
219
                )
220
            )
221
    except ImportError:
222
        raise AssertionError("Zipline installation requires pip")
223
224
    required = ('Cython', 'numpy')
225
    for line in module_requirements('etc/requirements.txt', required):
226
        pip.main(['install', line])
227
228
229
pre_setup()
230
231
conda_build = os.path.basename(sys.argv[0]) == 'conda-build'
232
233
setup(
234
    name='zipline',
235
    version=versioneer.get_version(),
236
    cmdclass=versioneer.get_cmdclass(),
237
    description='A backtester for financial algorithms.',
238
    author='Quantopian Inc.',
239
    author_email='[email protected]',
240
    packages=find_packages('.', include=['zipline', 'zipline.*']),
241
    ext_modules=ext_modules,
242
    scripts=['scripts/run_algo.py'],
243
    include_package_data=True,
244
    license='Apache 2.0',
245
    classifiers=[
246
        'Development Status :: 4 - Beta',
247
        'License :: OSI Approved :: Apache Software License',
248
        'Natural Language :: English',
249
        'Programming Language :: Python',
250
        'Programming Language :: Python :: 2.7',
251
        'Programming Language :: Python :: 3.3',
252
        'Programming Language :: Python :: 3.4',
253
        'Operating System :: OS Independent',
254
        'Intended Audience :: Science/Research',
255
        'Topic :: Office/Business :: Financial',
256
        'Topic :: Scientific/Engineering :: Information Analysis',
257
        'Topic :: System :: Distributed Computing',
258
    ],
259
    install_requires=install_requires(strict_bounds=conda_build,
260
                                      conda_format=conda_build),
261
    extras_require=extras_requires(conda_format=conda_build),
262
    url="http://zipline.io",
263
)
264