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