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