##// END OF EJS Templates
remove nonfunctional `-la` short arg in cython magic...
MinRK -
Show More
@@ -1,307 +1,307
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 =====================
4 4 Cython related magics
5 5 =====================
6 6
7 7 Usage
8 8 =====
9 9
10 10 ``%%cython``
11 11
12 12 {CYTHON_DOC}
13 13
14 14 ``%%cython_inline``
15 15
16 16 {CYTHON_INLINE_DOC}
17 17
18 18 ``%%cython_pyximport``
19 19
20 20 {CYTHON_PYXIMPORT_DOC}
21 21
22 22 Author:
23 23 * Brian Granger
24 24
25 25 Parts of this code were taken from Cython.inline.
26 26 """
27 27 #-----------------------------------------------------------------------------
28 28 # Copyright (C) 2010-2011, IPython Development Team.
29 29 #
30 30 # Distributed under the terms of the Modified BSD License.
31 31 #
32 32 # The full license is in the file COPYING.txt, distributed with this software.
33 33 #-----------------------------------------------------------------------------
34 34
35 35 from __future__ import print_function
36 36
37 37 import imp
38 38 import io
39 39 import os
40 40 import re
41 41 import sys
42 42 import time
43 43
44 44 try:
45 45 import hashlib
46 46 except ImportError:
47 47 import md5 as hashlib
48 48
49 49 from distutils.core import Distribution, Extension
50 50 from distutils.command.build_ext import build_ext
51 51
52 52 from IPython.core import display
53 53 from IPython.core import magic_arguments
54 54 from IPython.core.magic import Magics, magics_class, cell_magic
55 55 from IPython.testing.skipdoctest import skip_doctest
56 56 from IPython.utils import py3compat
57 57
58 58 import Cython
59 59 from Cython.Compiler.Errors import CompileError
60 60 from Cython.Build.Dependencies import cythonize
61 61
62 62
63 63 @magics_class
64 64 class CythonMagics(Magics):
65 65
66 66 def __init__(self, shell):
67 67 super(CythonMagics,self).__init__(shell)
68 68 self._reloads = {}
69 69 self._code_cache = {}
70 70
71 71 def _import_all(self, module):
72 72 for k,v in module.__dict__.items():
73 73 if not k.startswith('__'):
74 74 self.shell.push({k:v})
75 75
76 76 @cell_magic
77 77 def cython_inline(self, line, cell):
78 78 """Compile and run a Cython code cell using Cython.inline.
79 79
80 80 This magic simply passes the body of the cell to Cython.inline
81 81 and returns the result. If the variables `a` and `b` are defined
82 82 in the user's namespace, here is a simple example that returns
83 83 their sum::
84 84
85 85 %%cython_inline
86 86 return a+b
87 87
88 88 For most purposes, we recommend the usage of the `%%cython` magic.
89 89 """
90 90 locs = self.shell.user_global_ns
91 91 globs = self.shell.user_ns
92 92 return Cython.inline(cell, locals=locs, globals=globs)
93 93
94 94 @cell_magic
95 95 def cython_pyximport(self, line, cell):
96 96 """Compile and import a Cython code cell using pyximport.
97 97
98 98 The contents of the cell are written to a `.pyx` file in the current
99 99 working directory, which is then imported using `pyximport`. This
100 100 magic requires a module name to be passed::
101 101
102 102 %%cython_pyximport modulename
103 103 def f(x):
104 104 return 2.0*x
105 105
106 106 The compiled module is then imported and all of its symbols are
107 107 injected into the user's namespace. For most purposes, we recommend
108 108 the usage of the `%%cython` magic.
109 109 """
110 110 module_name = line.strip()
111 111 if not module_name:
112 112 raise ValueError('module name must be given')
113 113 fname = module_name + '.pyx'
114 114 with io.open(fname, 'w', encoding='utf-8') as f:
115 115 f.write(cell)
116 116 if 'pyximport' not in sys.modules:
117 117 import pyximport
118 118 pyximport.install(reload_support=True)
119 119 if module_name in self._reloads:
120 120 module = self._reloads[module_name]
121 121 reload(module)
122 122 else:
123 123 __import__(module_name)
124 124 module = sys.modules[module_name]
125 125 self._reloads[module_name] = module
126 126 self._import_all(module)
127 127
128 128 @magic_arguments.magic_arguments()
129 129 @magic_arguments.argument(
130 130 '-c', '--compile-args', action='append', default=[],
131 131 help="Extra flags to pass to compiler via the `extra_compile_args` "
132 132 "Extension flag (can be specified multiple times)."
133 133 )
134 134 @magic_arguments.argument(
135 '-la', '--link-args', action='append', default=[],
135 '--link-args', action='append', default=[],
136 136 help="Extra flags to pass to linker via the `extra_link_args` "
137 137 "Extension flag (can be specified multiple times)."
138 138 )
139 139 @magic_arguments.argument(
140 140 '-l', '--lib', action='append', default=[],
141 141 help="Add a library to link the extension against (can be specified "
142 142 "multiple times)."
143 143 )
144 144 @magic_arguments.argument(
145 145 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
146 146 help="Add a path to the list of libary directories (can be specified "
147 147 "multiple times)."
148 148 )
149 149 @magic_arguments.argument(
150 150 '-I', '--include', action='append', default=[],
151 151 help="Add a path to the list of include directories (can be specified "
152 152 "multiple times)."
153 153 )
154 154 @magic_arguments.argument(
155 155 '-+', '--cplus', action='store_true', default=False,
156 156 help="Output a C++ rather than C file."
157 157 )
158 158 @magic_arguments.argument(
159 159 '-f', '--force', action='store_true', default=False,
160 160 help="Force the compilation of a new module, even if the source has been "
161 161 "previously compiled."
162 162 )
163 163 @magic_arguments.argument(
164 164 '-a', '--annotate', action='store_true', default=False,
165 165 help="Produce a colorized HTML version of the source."
166 166 )
167 167 @cell_magic
168 168 def cython(self, line, cell):
169 169 """Compile and import everything from a Cython code cell.
170 170
171 171 The contents of the cell are written to a `.pyx` file in the
172 172 directory `IPYTHONDIR/cython` using a filename with the hash of the
173 173 code. This file is then cythonized and compiled. The resulting module
174 174 is imported and all of its symbols are injected into the user's
175 175 namespace. The usage is similar to that of `%%cython_pyximport` but
176 176 you don't have to pass a module name::
177 177
178 178 %%cython
179 179 def f(x):
180 180 return 2.0*x
181 181
182 182 To compile OpenMP codes, pass the required `--compile-args`
183 183 and `--link-args`. For example with gcc::
184 184
185 185 %%cython --compile-args=-fopenmp --link-args=-fopenmp
186 186 ...
187 187 """
188 188 args = magic_arguments.parse_argstring(self.cython, line)
189 189 code = cell if cell.endswith('\n') else cell+'\n'
190 190 lib_dir = os.path.join(self.shell.ipython_dir, 'cython')
191 191 quiet = True
192 192 key = code, sys.version_info, sys.executable, Cython.__version__
193 193
194 194 if not os.path.exists(lib_dir):
195 195 os.makedirs(lib_dir)
196 196
197 197 if args.force:
198 198 # Force a new module name by adding the current time to the
199 199 # key which is hashed to determine the module name.
200 200 key += time.time(),
201 201
202 202 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
203 203 module_path = os.path.join(lib_dir, module_name + self.so_ext)
204 204
205 205 have_module = os.path.isfile(module_path)
206 206 need_cythonize = not have_module
207 207
208 208 if args.annotate:
209 209 html_file = os.path.join(lib_dir, module_name + '.html')
210 210 if not os.path.isfile(html_file):
211 211 need_cythonize = True
212 212
213 213 if need_cythonize:
214 214 c_include_dirs = args.include
215 215 if 'numpy' in code:
216 216 import numpy
217 217 c_include_dirs.append(numpy.get_include())
218 218 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
219 219 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
220 220 with io.open(pyx_file, 'w', encoding='utf-8') as f:
221 221 f.write(code)
222 222 extension = Extension(
223 223 name = module_name,
224 224 sources = [pyx_file],
225 225 include_dirs = c_include_dirs,
226 226 library_dirs = args.library_dirs,
227 227 extra_compile_args = args.compile_args,
228 228 extra_link_args = args.link_args,
229 229 libraries = args.lib,
230 230 language = 'c++' if args.cplus else 'c',
231 231 )
232 232 build_extension = self._get_build_extension()
233 233 try:
234 234 opts = dict(
235 235 quiet=quiet,
236 236 annotate = args.annotate,
237 237 force = True,
238 238 )
239 239 build_extension.extensions = cythonize([extension], **opts)
240 240 except CompileError:
241 241 return
242 242
243 243 if not have_module:
244 244 build_extension.build_temp = os.path.dirname(pyx_file)
245 245 build_extension.build_lib = lib_dir
246 246 build_extension.run()
247 247 self._code_cache[key] = module_name
248 248
249 249 module = imp.load_dynamic(module_name, module_path)
250 250 self._import_all(module)
251 251
252 252 if args.annotate:
253 253 try:
254 254 with io.open(html_file, encoding='utf-8') as f:
255 255 annotated_html = f.read()
256 256 except IOError as e:
257 257 # File could not be opened. Most likely the user has a version
258 258 # of Cython before 0.15.1 (when `cythonize` learned the
259 259 # `force` keyword argument) and has already compiled this
260 260 # exact source without annotation.
261 261 print('Cython completed successfully but the annotated '
262 262 'source could not be read.', file=sys.stderr)
263 263 print(e, file=sys.stderr)
264 264 else:
265 265 return display.HTML(self.clean_annotated_html(annotated_html))
266 266
267 267 @property
268 268 def so_ext(self):
269 269 """The extension suffix for compiled modules."""
270 270 try:
271 271 return self._so_ext
272 272 except AttributeError:
273 273 self._so_ext = self._get_build_extension().get_ext_filename('')
274 274 return self._so_ext
275 275
276 276 def _get_build_extension(self):
277 277 dist = Distribution()
278 278 config_files = dist.find_config_files()
279 279 try:
280 280 config_files.remove('setup.cfg')
281 281 except ValueError:
282 282 pass
283 283 dist.parse_config_files(config_files)
284 284 build_extension = build_ext(dist)
285 285 build_extension.finalize_options()
286 286 return build_extension
287 287
288 288 @staticmethod
289 289 def clean_annotated_html(html):
290 290 """Clean up the annotated HTML source.
291 291
292 292 Strips the link to the generated C or C++ file, which we do not
293 293 present to the user.
294 294 """
295 295 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
296 296 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
297 297 return html
298 298
299 299 __doc__ = __doc__.format(
300 300 CYTHON_DOC = ' '*8 + CythonMagics.cython.__doc__,
301 301 CYTHON_INLINE_DOC = ' '*8 + CythonMagics.cython_inline.__doc__,
302 302 CYTHON_PYXIMPORT_DOC = ' '*8 + CythonMagics.cython_pyximport.__doc__,
303 303 )
304 304
305 305 def load_ipython_extension(ip):
306 306 """Load the extension in IPython."""
307 307 ip.register_magics(CythonMagics)
General Comments 0
You need to be logged in to leave comments. Login now