##// END OF EJS Templates
ctx is ignored by Cython > 0.13.
Bradley M. Froehle -
Show More
@@ -1,198 +1,195 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Cython related magics.
4 4
5 5 Author:
6 6 * Brian Granger
7 7
8 8 Parts of this code were taken from Cython.inline.
9 9 """
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2010-2011, IPython Development Team.
12 12 #
13 13 # Distributed under the terms of the Modified BSD License.
14 14 #
15 15 # The full license is in the file COPYING.txt, distributed with this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 import io
19 19 import os, sys
20 20 import imp
21 21
22 22 try:
23 23 import hashlib
24 24 except ImportError:
25 25 import md5 as hashlib
26 26
27 27 from distutils.core import Distribution, Extension
28 28 from distutils.command.build_ext import build_ext
29 29
30 30 from IPython.core import magic_arguments
31 31 from IPython.core.magic import Magics, magics_class, cell_magic
32 32 from IPython.testing.skipdoctest import skip_doctest
33 33 from IPython.utils import py3compat
34 34
35 35 import Cython
36 36 from Cython.Compiler.Errors import CompileError
37 from Cython.Compiler.Main import Context, default_options
38 37 from Cython.Build.Dependencies import cythonize
39 38
40 39
41 40 @magics_class
42 41 class CythonMagics(Magics):
43 42
44 43 def __init__(self, shell):
45 44 super(CythonMagics,self).__init__(shell)
46 45 self._reloads = {}
47 46 self._code_cache = {}
48 47
49 48 def _import_all(self, module):
50 49 for k,v in module.__dict__.items():
51 50 if not k.startswith('__'):
52 51 self.shell.push({k:v})
53 52
54 53 @cell_magic
55 54 def cython_inline(self, line, cell):
56 55 """Compile and run a Cython code cell using Cython.inline.
57 56
58 57 This magic simply passes the body of the cell to Cython.inline
59 58 and returns the result. If the variables `a` and `b` are defined
60 59 in the user's namespace, here is a simple example that returns
61 60 their sum::
62 61
63 62 %%cython_inline
64 63 return a+b
65 64
66 65 For most purposes, we recommend the usage of the `%%cython` magic.
67 66 """
68 67 locs = self.shell.user_global_ns
69 68 globs = self.shell.user_ns
70 69 return Cython.inline(cell, locals=locs, globals=globs)
71 70
72 71 @cell_magic
73 72 def cython_pyximport(self, line, cell):
74 73 """Compile and import a Cython code cell using pyximport.
75 74
76 75 The contents of the cell are written to a `.pyx` file in the current
77 76 working directory, which is then imported using `pyximport`. This
78 77 magic requires a module name to be passed::
79 78
80 79 %%cython_pyximport modulename
81 80 def f(x):
82 81 return 2.0*x
83 82
84 83 The compiled module is then imported and all of its symbols are injected into
85 84 the user's namespace. For most purposes, we recommend the usage of the
86 85 `%%cython` magic.
87 86 """
88 87 module_name = line.strip()
89 88 if not module_name:
90 89 raise ValueError('module name must be given')
91 90 fname = module_name + '.pyx'
92 91 with io.open(fname, 'w', encoding='utf-8') as f:
93 92 f.write(cell)
94 93 if 'pyximport' not in sys.modules:
95 94 import pyximport
96 95 pyximport.install(reload_support=True)
97 96 if module_name in self._reloads:
98 97 module = self._reloads[module_name]
99 98 reload(module)
100 99 else:
101 100 __import__(module_name)
102 101 module = sys.modules[module_name]
103 102 self._reloads[module_name] = module
104 103 self._import_all(module)
105 104
106 105 @magic_arguments.magic_arguments()
107 106 @magic_arguments.argument(
108 107 '-c', '--compile-args', action='append', default=[],
109 108 help="Extra flags to pass to compiler via the `extra_compile_args` Extension flag (can be specified multiple times)."
110 109 )
111 110 @magic_arguments.argument(
112 111 '-l', '--lib', action='append', default=[],
113 112 help="Add a library to link the extension against (can be specified multiple times)."
114 113 )
115 114 @magic_arguments.argument(
116 115 '-I', '--include', action='append', default=[],
117 116 help="Add a path to the list of include directories (can be specified multiple times)."
118 117 )
119 118 @magic_arguments.argument(
120 119 '-f', '--force', action='store_true', default=False,
121 120 help="Force the compilation of the pyx module even if it hasn't changed"
122 121 )
123 122 @cell_magic
124 123 def cython(self, line, cell):
125 124 """Compile and import everything from a Cython code cell.
126 125
127 126 The contents of the cell are written to a `.pyx` file in the
128 127 directory `IPYTHONDIR/cython` using a filename with the hash of the code.
129 128 This file is then cythonized and compiled. The resulting module
130 129 is imported and all of its symbols are injected into the user's
131 130 namespace. The usage is similar to that of `%%cython_pyximport` but
132 131 you don't have to pass a module name::
133 132
134 133 %%cython
135 134 def f(x):
136 135 return 2.0*x
137 136 """
138 137 args = magic_arguments.parse_argstring(self.cython, line)
139 138 code = cell if cell.endswith('\n') else cell+'\n'
140 139 lib_dir = os.path.join(self.shell.ipython_dir, 'cython')
141 cython_include_dirs = ['.']
142 140 force = args.force
143 141 quiet = True
144 ctx = Context(cython_include_dirs, default_options)
145 142 key = code, sys.version_info, sys.executable, Cython.__version__
146 143 module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
147 144 so_ext = [ ext for ext,_,mod_type in imp.get_suffixes() if mod_type == imp.C_EXTENSION ][0]
148 145 module_path = os.path.join(lib_dir, module_name+so_ext)
149 146
150 147 if not os.path.exists(lib_dir):
151 148 os.makedirs(lib_dir)
152 149
153 150 if force or not os.path.isfile(module_path):
154 151 c_include_dirs = args.include
155 152 if 'numpy' in code:
156 153 import numpy
157 154 c_include_dirs.append(numpy.get_include())
158 155 pyx_file = os.path.join(lib_dir, module_name + '.pyx')
159 156 pyx_file = py3compat.cast_bytes_py2(pyx_file, encoding=sys.getfilesystemencoding())
160 157 with io.open(pyx_file, 'w', encoding='utf-8') as f:
161 158 f.write(code)
162 159 extension = Extension(
163 160 name = module_name,
164 161 sources = [pyx_file],
165 162 include_dirs = c_include_dirs,
166 163 extra_compile_args = args.compile_args,
167 164 libraries = args.lib,
168 165 )
169 166 dist = Distribution()
170 167 config_files = dist.find_config_files()
171 168 try:
172 169 config_files.remove('setup.cfg')
173 170 except ValueError:
174 171 pass
175 172 dist.parse_config_files(config_files)
176 173 build_extension = build_ext(dist)
177 174 build_extension.finalize_options()
178 175 try:
179 build_extension.extensions = cythonize([extension], ctx=ctx, quiet=quiet)
176 build_extension.extensions = cythonize([extension], quiet=quiet)
180 177 except CompileError:
181 178 return
182 179 build_extension.build_temp = os.path.dirname(pyx_file)
183 180 build_extension.build_lib = lib_dir
184 181 build_extension.run()
185 182 self._code_cache[key] = module_name
186 183
187 184 module = imp.load_dynamic(module_name, module_path)
188 185 self._import_all(module)
189 186
190 187
191 188 _loaded = False
192 189
193 190 def load_ipython_extension(ip):
194 191 """Load the extension in IPython."""
195 192 global _loaded
196 193 if not _loaded:
197 194 ip.register_magics(CythonMagics)
198 195 _loaded = True
General Comments 0
You need to be logged in to leave comments. Login now