2020from setuptools .command .build_ext import build_ext
2121
2222
23+ CFLAGS = ['-O2' ]
2324LIBUV_DIR = os .path .join (os .path .dirname (__file__ ), 'vendor' , 'libuv' )
2425
2526
@@ -30,17 +31,149 @@ def discover_tests():
3031
3132
3233class libuv_build_ext (build_ext ):
33- build_ext .user_options .extend ([
34- ("use-system-libuv" , None ,
35- "Use the system provided libuv, instead of the bundled one" )
36- ])
34+ user_options = build_ext .user_options + [
35+ ('cython-always' , None ,
36+ 'run cythonize() even if .c files are present' ),
37+ ('cython-annotate' , None ,
38+ 'Produce a colorized HTML version of the Cython source.' ),
39+ ('cython-directives=' , None ,
40+ 'Cythion compiler directives' ),
41+ ('use-system-libuv' , None ,
42+ 'Use the system provided libuv, instead of the bundled one' ),
43+ ]
3744
38- build_ext .boolean_options .extend (["use-system-libuv" ])
45+ boolean_options = build_ext .boolean_options + [
46+ 'cython-always' ,
47+ 'cython-annotate' ,
48+ 'use-system-libuv' ,
49+ ]
3950
4051 def initialize_options (self ):
41- build_ext .initialize_options (self )
42- if getattr (self , 'use_system_libuv' , None ) is None :
43- self .use_system_libuv = 0
52+ super ().initialize_options ()
53+ self .use_system_libuv = False
54+ self .cython_always = False
55+ self .cython_annotate = None
56+ self .cython_directives = None
57+
58+ def finalize_options (self ):
59+ need_cythonize = self .cython_always
60+ cfiles = {}
61+
62+ for extension in self .distribution .ext_modules :
63+ for i , sfile in enumerate (extension .sources ):
64+ if sfile .endswith ('.pyx' ):
65+ prefix , ext = os .path .splitext (sfile )
66+ cfile = prefix + '.c'
67+
68+ if os .path .exists (cfile ) and not self .cython_always :
69+ extension .sources [i ] = cfile
70+ else :
71+ if os .path .exists (cfile ):
72+ cfiles [cfile ] = os .path .getmtime (cfile )
73+ else :
74+ cfiles [cfile ] = 0
75+ need_cythonize = True
76+
77+ if need_cythonize :
78+ try :
79+ import Cython
80+ except ImportError :
81+ raise RuntimeError (
82+ 'please install Cython to compile asyncpg from source' )
83+
84+ if Cython .__version__ < '0.24' :
85+ raise RuntimeError (
86+ 'uvloop requires Cython version 0.24 or greater' )
87+
88+ from Cython .Build import cythonize
89+
90+ directives = {}
91+ if self .cython_directives :
92+ for directive in self .cython_directives .split (',' ):
93+ k , _ , v = directive .partition ('=' )
94+ if v .lower () == 'false' :
95+ v = False
96+ if v .lower () == 'true' :
97+ v = True
98+
99+ directives [k ] = v
100+
101+ self .distribution .ext_modules [:] = cythonize (
102+ self .distribution .ext_modules ,
103+ compiler_directives = directives ,
104+ annotate = self .cython_annotate )
105+
106+ for cfile , timestamp in cfiles .items ():
107+ if os .path .getmtime (cfile ) != timestamp :
108+ # The file was recompiled, patch
109+ self ._patch_cfile (cfile )
110+
111+ super ().finalize_options ()
112+
113+ def _patch_cfile (self , cfile ):
114+ # Patch Cython 'async def' coroutines to have a 'tp_iter'
115+ # slot, which makes them compatible with 'yield from' without
116+ # the `asyncio.coroutine` decorator.
117+
118+ with open (cfile , 'rt' ) as f :
119+ src = f .read ()
120+
121+ src = re .sub (
122+ r'''
123+ \s* offsetof\(__pyx_CoroutineObject,\s*gi_weakreflist\),
124+ \s* 0,
125+ \s* 0,
126+ \s* __pyx_Coroutine_methods,
127+ \s* __pyx_Coroutine_memberlist,
128+ \s* __pyx_Coroutine_getsets,
129+ ''' ,
130+
131+ r'''
132+ offsetof(__pyx_CoroutineObject, gi_weakreflist),
133+ __Pyx_Coroutine_await, /* tp_iter */
134+ 0,
135+ __pyx_Coroutine_methods,
136+ __pyx_Coroutine_memberlist,
137+ __pyx_Coroutine_getsets,
138+ ''' ,
139+
140+ src , flags = re .X )
141+
142+ # Fix a segfault in Cython.
143+ src = re .sub (
144+ r'''
145+ \s* __Pyx_Coroutine_get_qualname\(__pyx_CoroutineObject\s+\*self\)
146+ \s* {
147+ \s* Py_INCREF\(self->gi_qualname\);
148+ ''' ,
149+
150+ r'''
151+ __Pyx_Coroutine_get_qualname(__pyx_CoroutineObject *self)
152+ {
153+ if (self->gi_qualname == NULL) { return __pyx_empty_unicode; }
154+ Py_INCREF(self->gi_qualname);
155+ ''' ,
156+
157+ src , flags = re .X )
158+
159+ src = re .sub (
160+ r'''
161+ \s* __Pyx_Coroutine_get_name\(__pyx_CoroutineObject\s+\*self\)
162+ \s* {
163+ \s* Py_INCREF\(self->gi_name\);
164+ ''' ,
165+
166+ r'''
167+ __Pyx_Coroutine_get_name(__pyx_CoroutineObject *self)
168+ {
169+ if (self->gi_name == NULL) { return __pyx_empty_unicode; }
170+ Py_INCREF(self->gi_name);
171+ ''' ,
172+
173+ src , flags = re .X )
174+
175+ with open (cfile , 'wt' ) as f :
176+ f .write (src )
44177
45178 def build_libuv (self ):
46179 env = os .environ .copy ()
@@ -112,9 +245,9 @@ def build_extensions(self):
112245 Extension (
113246 "uvloop.loop" ,
114247 sources = [
115- "uvloop/loop.c " ,
248+ "uvloop/loop.pyx " ,
116249 ],
117- extra_compile_args = [ '-O2' ]
250+ extra_compile_args = CFLAGS
118251 ),
119252 ],
120253 classifiers = [
0 commit comments