##// END OF EJS Templates
Adding Cython extension and example notebook.
Adding Cython extension and example notebook.

File last commit:

r7031:3024e635
r7031:3024e635
Show More
cythonmagic.py
145 lines | 4.7 KiB | text/x-python | PythonLexer
# -*- coding: utf-8 -*-
"""
Cython related magics.
Author:
* Brian Granger
Parts of this code were taken from Cython.inline.
"""
#-----------------------------------------------------------------------------
# Copyright (C) 2010-2011, IPython Development Team.
#
# Distributed under the terms of the Modified BSD License.
#
# The full license is in the file COPYING.txt, distributed with this software.
#-----------------------------------------------------------------------------
import os, sys
from importlib import import_module
import imp
try:
import hashlib
except ImportError:
import md5 as hashlib
from distutils.core import Distribution, Extension
from distutils.command.build_ext import build_ext
from IPython.core.magic import Magics, magics_class, cell_magic
from IPython.testing.skipdoctest import skip_doctest
from IPython.core.magic_arguments import (
argument, magic_arguments, parse_argstring
)
import Cython
from Cython.Compiler.Errors import CompileError
from Cython.Compiler.Main import Context, default_options
from Cython.Build.Dependencies import cythonize
@magics_class
class CythonMagics(Magics):
def __init__(self, shell):
super(CythonMagics,self).__init__(shell)
self._reloads = {}
self._code_cache = {}
def _import_all(self, module):
for k,v in module.__dict__.items():
if not k.startswith('__'):
self.shell.push({k:v})
@skip_doctest
@cell_magic
def cython_inline(self, line, cell):
"""Compile and run a Cython code cell using Cython.inline."""
locs = self.shell.user_global_ns
globs = self.shell.user_ns
return Cython.inline(cell, locals=locs, globals=globs)
@skip_doctest
@cell_magic
def cython_pyximport(self, line, cell):
"""Compile and import a Cython code cell using pyximport."""
module_name = line.strip()
if not module_name:
raise ValueError('module name must be given')
fname = module_name + '.pyx'
with open(fname, 'w') as f:
f.write(cell)
if 'pyximport' not in sys.modules:
import pyximport
pyximport.install(reload_support=True)
if module_name in self._reloads:
module = self._reloads[module_name]
reload(module)
else:
module = import_module(module_name)
self._reloads[module_name] = module
self._import_all(module)
@magic_arguments()
@argument(
'-f', '--force', action='store_true', default=False,
help="Force the compilation of the pyx module even if it hasn't changed"
)
@skip_doctest
@cell_magic
def cython(self, line, cell):
"""Compile and import everything from a Cython code cell."""
args = parse_argstring(self.cython, line)
code = cell if cell.endswith('\n') else cell+'\n'
lib_dir=os.path.expanduser('~/.cython/magic')
cython_include_dirs=['.']
force=args.force
quiet=True
ctx = Context(cython_include_dirs, default_options)
key = code, sys.version_info, sys.executable, Cython.__version__
module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest()
so_ext = [ ext for ext,_,mod_type in imp.get_suffixes() if mod_type == imp.C_EXTENSION ][0]
module_path = os.path.join(lib_dir, module_name+so_ext)
if not os.path.exists(lib_dir):
os.makedirs(lib_dir)
if force or not os.path.isfile(module_path):
cflags = []
c_include_dirs = []
if 'numpy' in code:
import numpy
c_include_dirs.append(numpy.get_include())
pyx_file = os.path.join(lib_dir, module_name + '.pyx')
with open(pyx_file, 'w') as f:
f.write(code)
extension = Extension(
name = module_name,
sources = [pyx_file],
include_dirs = c_include_dirs,
extra_compile_args = cflags
)
build_extension = build_ext(Distribution())
build_extension.finalize_options()
try:
build_extension.extensions = cythonize([extension], ctx=ctx, quiet=quiet)
except CompileError:
return
build_extension.build_temp = os.path.dirname(pyx_file)
build_extension.build_lib = lib_dir
build_extension.run()
self._code_cache[key] = module_name
module = imp.load_dynamic(module_name, module_path)
self._import_all(module)
_loaded = False
def load_ipython_extension(ip):
"""Load the extension in IPython."""
global _loaded
if not _loaded:
ip.register_magics(CythonMagics)
_loaded = True