##// END OF EJS Templates
Merge pull request #6510 from Carreau/removecython...
Thomas Kluyver -
r18304:dbd30bfa merge
parent child Browse files
Show More
@@ -0,0 +1,1 b''
1 * The ``%cython`` magic, is now part of the Cython module. Use `%load_ext Cython` with a version of Cython >= 0.21 to have access to the magic now.
@@ -1,37 +1,10 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 =====================
4 Cython related magics
5 =====================
3 The cython magic has been integrated into Cython itself,
4 which is now released in version 0.21.
6 5
7 Magic command interface for interactive work with Cython
8
9 .. note::
10
11 The ``Cython`` package needs to be installed separately. It
12 can be obtained using ``easy_install`` or ``pip``.
13
14 Usage
15 =====
16
17 To enable the magics below, execute ``%load_ext cythonmagic``.
18
19 ``%%cython``
20
21 {CYTHON_DOC}
22
23 ``%%cython_inline``
24
25 {CYTHON_INLINE_DOC}
26
27 ``%%cython_pyximport``
28
29 {CYTHON_PYXIMPORT_DOC}
30
31 Author:
32 * Brian Granger
33
34 Parts of this code were taken from Cython.inline.
6 cf github `Cython` organisation, `Cython` repo, under the
7 file `Cython/Build/IpythonMagic.py`
35 8 """
36 9 #-----------------------------------------------------------------------------
37 10 # Copyright (C) 2010-2011, IPython Development Team.
@@ -43,303 +16,28 b' Parts of this code were taken from Cython.inline.'
43 16
44 17 from __future__ import print_function
45 18
46 import imp
47 import io
48 import os
49 import re
50 import sys
51 import time
19 import IPython.utils.version as version
52 20
53 21 try:
54 reload
55 except NameError: # Python 3
56 from imp import reload
22 import Cython
23 except:
24 Cython = None
57 25
58 26 try:
59 import hashlib
60 except ImportError:
61 import md5 as hashlib
62
63 from distutils.core import Distribution, Extension
64 from distutils.command.build_ext import build_ext
65
66 from IPython.core import display
67 from IPython.core import magic_arguments
68 from IPython.core.magic import Magics, magics_class, cell_magic
69 from IPython.utils import py3compat
70 from IPython.utils.path import get_ipython_cache_dir
71 from IPython.utils.text import dedent
72
73 import Cython
74 from Cython.Compiler.Errors import CompileError
75 from Cython.Build.Dependencies import cythonize
76
77
78 @magics_class
79 class CythonMagics(Magics):
80
81 def __init__(self, shell):
82 super(CythonMagics,self).__init__(shell)
83 self._reloads = {}
84 self._code_cache = {}
85
86 def _import_all(self, module):
87 for k,v in module.__dict__.items():
88 if not k.startswith('__'):
89 self.shell.push({k:v})
90
91 @cell_magic
92 def cython_inline(self, line, cell):
93 """Compile and run a Cython code cell using Cython.inline.
94
95 This magic simply passes the body of the cell to Cython.inline
96 and returns the result. If the variables `a` and `b` are defined
97 in the user's namespace, here is a simple example that returns
98 their sum::
99
100 %%cython_inline
101 return a+b
102
103 For most purposes, we recommend the usage of the `%%cython` magic.
104 """
105 locs = self.shell.user_global_ns
106 globs = self.shell.user_ns
107 return Cython.inline(cell, locals=locs, globals=globs)
108
109 @cell_magic
110 def cython_pyximport(self, line, cell):
111 """Compile and import a Cython code cell using pyximport.
27 from Cython.Build.IpythonMagic import CythonMagics
28 except :
29 pass
112 30
113 The contents of the cell are written to a `.pyx` file in the current
114 working directory, which is then imported using `pyximport`. This
115 magic requires a module name to be passed::
116
117 %%cython_pyximport modulename
118 def f(x):
119 return 2.0*x
120
121 The compiled module is then imported and all of its symbols are
122 injected into the user's namespace. For most purposes, we recommend
123 the usage of the `%%cython` magic.
124 """
125 module_name = line.strip()
126 if not module_name:
127 raise ValueError('module name must be given')
128 fname = module_name + '.pyx'
129 with io.open(fname, 'w', encoding='utf-8') as f:
130 f.write(cell)
131 if 'pyximport' not in sys.modules:
132 import pyximport
133 pyximport.install(reload_support=True)
134 if module_name in self._reloads:
135 module = self._reloads[module_name]
136 reload(module)
137 else:
138 __import__(module_name)
139 module = sys.modules[module_name]
140 self._reloads[module_name] = module
141 self._import_all(module)
142
143 @magic_arguments.magic_arguments()
144 @magic_arguments.argument(
145 '-c', '--compile-args', action='append', default=[],
146 help="Extra flags to pass to compiler via the `extra_compile_args` "
147 "Extension flag (can be specified multiple times)."
148 )
149 @magic_arguments.argument(
150 '--link-args', action='append', default=[],
151 help="Extra flags to pass to linker via the `extra_link_args` "
152 "Extension flag (can be specified multiple times)."
153 )
154 @magic_arguments.argument(
155 '-l', '--lib', action='append', default=[],
156 help="Add a library to link the extension against (can be specified "
157 "multiple times)."
158 )
159 @magic_arguments.argument(
160 '-n', '--name',
161 help="Specify a name for the Cython module."
162 )
163 @magic_arguments.argument(
164 '-L', dest='library_dirs', metavar='dir', action='append', default=[],
165 help="Add a path to the list of libary directories (can be specified "
166 "multiple times)."
167 )
168 @magic_arguments.argument(
169 '-I', '--include', action='append', default=[],
170 help="Add a path to the list of include directories (can be specified "
171 "multiple times)."
172 )
173 @magic_arguments.argument(
174 '-+', '--cplus', action='store_true', default=False,
175 help="Output a C++ rather than C file."
176 )
177 @magic_arguments.argument(
178 '-f', '--force', action='store_true', default=False,
179 help="Force the compilation of a new module, even if the source has been "
180 "previously compiled."
181 )
182 @magic_arguments.argument(
183 '-a', '--annotate', action='store_true', default=False,
184 help="Produce a colorized HTML version of the source."
185 )
186 @cell_magic
187 def cython(self, line, cell):
188 """Compile and import everything from a Cython code cell.
189
190 The contents of the cell are written to a `.pyx` file in the
191 directory `IPYTHONDIR/cython` using a filename with the hash of the
192 code. This file is then cythonized and compiled. The resulting module
193 is imported and all of its symbols are injected into the user's
194 namespace. The usage is similar to that of `%%cython_pyximport` but
195 you don't have to pass a module name::
196
197 %%cython
198 def f(x):
199 return 2.0*x
200
201 To compile OpenMP codes, pass the required `--compile-args`
202 and `--link-args`. For example with gcc::
203
204 %%cython --compile-args=-fopenmp --link-args=-fopenmp
205 ...
206 """
207 args = magic_arguments.parse_argstring(self.cython, line)
208 code = cell if cell.endswith('\n') else cell+'\n'
209 lib_dir = os.path.join(get_ipython_cache_dir(), 'cython')
210 quiet = True
211 key = code, sys.version_info, sys.executable, Cython.__version__
212
213 if not os.path.exists(lib_dir):
214 os.makedirs(lib_dir)
215
216 if args.force:
217 # Force a new module name by adding the current time to the
218 # key which is hashed to determine the module name.
219 key += time.time(),
220
221 if args.name:
222 module_name = py3compat.unicode_to_str(args.name)
223 else:
224 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
225 module_path = os.path.join(lib_dir, module_name + self.so_ext)
226
227 have_module = os.path.isfile(module_path)
228 need_cythonize = not have_module
229
230 if args.annotate:
231 html_file = os.path.join(lib_dir, module_name + '.html')
232 if not os.path.isfile(html_file):
233 need_cythonize = True
234
235 if need_cythonize:
236 c_include_dirs = args.include
237 if 'numpy' in code:
238 import numpy
239 c_include_dirs.append(numpy.get_include())
240 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
241 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
242 with io.open(pyx_file, 'w', encoding='utf-8') as f:
243 f.write(code)
244 extension = Extension(
245 name = module_name,
246 sources = [pyx_file],
247 include_dirs = c_include_dirs,
248 library_dirs = args.library_dirs,
249 extra_compile_args = args.compile_args,
250 extra_link_args = args.link_args,
251 libraries = args.lib,
252 language = 'c++' if args.cplus else 'c',
253 )
254 build_extension = self._get_build_extension()
255 try:
256 opts = dict(
257 quiet=quiet,
258 annotate = args.annotate,
259 force = True,
260 )
261 build_extension.extensions = cythonize([extension], **opts)
262 except CompileError:
263 return
264
265 if not have_module:
266 build_extension.build_temp = os.path.dirname(pyx_file)
267 build_extension.build_lib = lib_dir
268 build_extension.run()
269 self._code_cache[key] = module_name
270
271 module = imp.load_dynamic(module_name, module_path)
272 self._import_all(module)
273
274 if args.annotate:
275 try:
276 with io.open(html_file, encoding='utf-8') as f:
277 annotated_html = f.read()
278 except IOError as e:
279 # File could not be opened. Most likely the user has a version
280 # of Cython before 0.15.1 (when `cythonize` learned the
281 # `force` keyword argument) and has already compiled this
282 # exact source without annotation.
283 print('Cython completed successfully but the annotated '
284 'source could not be read.', file=sys.stderr)
285 print(e, file=sys.stderr)
286 else:
287 return display.HTML(self.clean_annotated_html(annotated_html))
288
289 @property
290 def so_ext(self):
291 """The extension suffix for compiled modules."""
292 try:
293 return self._so_ext
294 except AttributeError:
295 self._so_ext = self._get_build_extension().get_ext_filename('')
296 return self._so_ext
297
298 def _clear_distutils_mkpath_cache(self):
299 """clear distutils mkpath cache
300
301 prevents distutils from skipping re-creation of dirs that have been removed
302 """
303 try:
304 from distutils.dir_util import _path_created
305 except ImportError:
306 pass
307 else:
308 _path_created.clear()
309
310 def _get_build_extension(self):
311 self._clear_distutils_mkpath_cache()
312 dist = Distribution()
313 config_files = dist.find_config_files()
314 try:
315 config_files.remove('setup.cfg')
316 except ValueError:
317 pass
318 dist.parse_config_files(config_files)
319 build_extension = build_ext(dist)
320 build_extension.finalize_options()
321 return build_extension
322
323 @staticmethod
324 def clean_annotated_html(html):
325 """Clean up the annotated HTML source.
326
327 Strips the link to the generated C or C++ file, which we do not
328 present to the user.
329 """
330 r = re.compile('<p>Raw output: <a href="(.*)">(.*)</a>')
331 html = '\n'.join(l for l in html.splitlines() if not r.match(l))
332 return html
333
334 __doc__ = __doc__.format(
335 # rST doesn't see the -+ flag as part of an option list, so we
336 # hide it from the module-level docstring.
337 CYTHON_DOC = dedent(CythonMagics.cython.__doc__\
338 .replace('-+, --cplus','--cplus ')),
339 CYTHON_INLINE_DOC = dedent(CythonMagics.cython_inline.__doc__),
340 CYTHON_PYXIMPORT_DOC = dedent(CythonMagics.cython_pyximport.__doc__),
341 )
342 31
32 ## still load the magic in IPython 3.x, remove completely in future versions.
343 33 def load_ipython_extension(ip):
344 34 """Load the extension in IPython."""
345 ip.register_magics(CythonMagics)
35
36 print("""The Cython magic has been move to the Cython package, hence """)
37 print("""`%load_ext cythonmagic` is deprecated; Please use `%load_ext Cython` instead.""")
38
39 if Cython is None or not version.check_version(Cython.__version__, "0.21"):
40 print("You need Cython version >=0.21 to use the Cython magic")
41 return
42 print("""\nThough, because I am nice, I'll still try to load it for you this time.""")
43 Cython.load_ipython_extension(ip)
@@ -131,7 +131,6 b" have['pymongo'] = test_for('pymongo')"
131 131 have['pygments'] = test_for('pygments')
132 132 have['qt'] = test_for('IPython.external.qt')
133 133 have['sqlite3'] = test_for('sqlite3')
134 have['cython'] = test_for('Cython')
135 134 have['tornado'] = test_for('tornado.version_info', (3,1,0), callback=None)
136 135 have['jinja2'] = test_for('jinja2')
137 136 have['mistune'] = test_for('mistune')
@@ -240,9 +239,6 b" test_sections['kernel.inprocess'].requires('zmq')"
240 239
241 240 # extensions:
242 241 sec = test_sections['extensions']
243 if not have['cython']:
244 sec.exclude('cythonmagic')
245 sec.exclude('tests.test_cythonmagic')
246 242 # This is deprecated in favour of rpy2
247 243 sec.exclude('rmagic')
248 244 # autoreload does some strange stuff, so move it to its own test section
@@ -4,4 +4,4 b''
4 4 cythonmagic
5 5 ===========
6 6
7 .. automodule:: IPython.extensions.cythonmagic
7 The `cython` magic has been moved in the `Cython` package.
@@ -100,3 +100,5 b' Extensions bundled with IPython'
100 100 * ``rmagic`` is now part of `rpy2 <http://rpy.sourceforge.net/>`_. Use
101 101 ``%load_ext rpy2.ipython`` to load it, and see :mod:`rpy2.ipython.rmagic` for
102 102 details of how to use it.
103 * ``cythonmagic``used to be bundled, but is now part of `cython <https://github.com/cython/cython/>`_
104 Use ``%load_ext Cython`` to load it.
@@ -41,7 +41,7 b' extend, :meth:`~IPython.config.loader.LazyConfigValue.prepend` (like'
41 41 extend, but at the front), add and update (which works both for dicts
42 42 and sets)::
43 43
44 c.InteractiveShellApp.extensions.append('cythonmagic')
44 c.InteractiveShellApp.extensions.append('Cython')
45 45
46 46 .. versionadded:: 2.0
47 47 list, dict and set methods for config values
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now