forked from falconry/falcon
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsetup.py
211 lines (173 loc) · 5.81 KB
/
setup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
import glob
import io
import os
from os import path
import re
import sys
from setuptools import Extension, setup
MYDIR = path.abspath(os.path.dirname(__file__))
try:
sys.pypy_version_info
PYPY = True
except AttributeError:
PYPY = False
if PYPY:
CYTHON = False
else:
try:
from Cython.Distutils import build_ext
CYTHON = True
except ImportError:
CYTHON = False
class BuildFailed(Exception):
pass
def get_cython_options():
# from sqlalchemy setup.py
from distutils.errors import (
CCompilerError,
DistutilsExecError,
DistutilsPlatformError,
)
ext_errors = (CCompilerError, DistutilsExecError, DistutilsPlatformError)
if sys.platform == 'win32':
# Work around issue https://github.com/pypa/setuptools/issues/1902
ext_errors += (IOError, TypeError)
class ve_build_ext(build_ext):
# This class allows Cython building to fail.
def run(self):
try:
super().run()
except DistutilsPlatformError:
raise BuildFailed()
def build_extension(self, ext):
try:
super().build_extension(ext)
except ext_errors as e:
raise BuildFailed() from e
except ValueError as e:
# this can happen on Windows 64 bit, see Python issue 7511
if "'path'" in str(e):
raise BuildFailed() from e
raise
def list_modules(dirname, pattern):
filenames = glob.glob(path.join(dirname, pattern))
module_names = []
for name in filenames:
module, ext = path.splitext(path.basename(name))
if module != '__init__':
module_names.append((module, ext))
return module_names
package_names = [
'falcon',
'falcon.cyutil',
'falcon.media',
'falcon.routing',
'falcon.util',
'falcon.vendor.mimeparse',
]
modules_to_exclude = [
# NOTE(kgriffs): Cython does not handle dynamically-created async
# methods correctly.
# NOTE(vytas,kgriffs): We have to also avoid cythonizing several
# other functions that might make it so that the framework
# can not recognize them as coroutine functions.
#
# See also:
#
# * https://github.com/cython/cython/issues/2273
# * https://bugs.python.org/issue38225
#
# NOTE(vytas): It is pointless to cythonize reader.py, since cythonized
# Falcon is using reader.pyx instead.
'falcon.hooks',
'falcon.responders',
'falcon.util.reader',
'falcon.util.sync',
]
cython_package_names = frozenset(
[
'falcon.cyutil',
]
)
ext_modules = [
Extension(
package + '.' + module, [path.join(*(package.split('.') + [module + ext]))]
)
for package in package_names
for module, ext in list_modules(
path.join(MYDIR, *package.split('.')),
('*.pyx' if package in cython_package_names else '*.py'),
)
if (package + '.' + module) not in modules_to_exclude
]
# NOTE(vytas): Now that all our codebase is Python 3.7+, specify the
# Python 3 language level for Cython as well to avoid any surprises.
for ext_mod in ext_modules:
ext_mod.cython_directives = {'language_level': '3', 'annotation_typing': False}
cmdclass = {'build_ext': ve_build_ext}
return cmdclass, ext_modules
def load_description():
in_patron_list = False
in_patron_replacement = False
in_raw = False
description_lines = []
# NOTE(kgriffs): PyPI does not support the raw directive
for readme_line in io.open('README.rst', 'r', encoding='utf-8'):
# NOTE(vytas): The patron list largely builds upon raw sections
if readme_line.startswith('.. Patron list starts'):
in_patron_list = True
in_patron_replacement = True
continue
elif in_patron_list:
if not readme_line.strip():
in_patron_replacement = False
elif in_patron_replacement:
description_lines.append(readme_line.lstrip())
if readme_line.startswith('.. Patron list ends'):
in_patron_list = False
continue
elif readme_line.startswith('.. raw::'):
in_raw = True
elif in_raw:
if readme_line and not re.match(r'\s', readme_line):
in_raw = False
if not in_raw:
description_lines.append(readme_line)
return ''.join(description_lines)
def run_setup(CYTHON):
if CYTHON:
cmdclass, ext_modules = get_cython_options()
else:
cmdclass, ext_modules = {}, []
setup(
long_description=load_description(),
cmdclass=cmdclass,
ext_modules=ext_modules,
)
def status_msgs(*msgs):
print('*' * 75, *msgs, '*' * 75, sep='\n')
if not CYTHON:
run_setup(False)
if not PYPY:
status_msgs('Cython compilation not supported in this environment')
elif os.environ.get('FALCON_DISABLE_CYTHON'):
run_setup(False)
status_msgs(
'FALCON_DISABLE_CYTHON is set, skipping cython compilation.',
'Pure-Python build succeeded.',
)
else:
try:
run_setup(True)
except BuildFailed as exc:
status_msgs(
exc.__cause__,
'Cython compilation could not be completed, speedups are not enabled.',
'Failure information, if any, is above.',
'Retrying the build without the C extension now.',
)
run_setup(False)
status_msgs(
'Cython compilation could not be completed, speedups are not enabled.',
'Pure-Python build succeeded.',
)