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