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