Commit 68811fa9 authored by Stefan Behnel's avatar Stefan Behnel

support in-place building for pyximport

parent a4e6c41f
...@@ -21,7 +21,8 @@ DEBUG = 0 ...@@ -21,7 +21,8 @@ DEBUG = 0
_reloads={} _reloads={}
def pyx_to_dll(filename, ext = None, force_rebuild = 0, def pyx_to_dll(filename, ext = None, force_rebuild = 0,
build_in_temp=False, pyxbuild_dir=None, setup_args={}, reload_support=False): build_in_temp=False, pyxbuild_dir=None, setup_args={},
reload_support=False, inplace=False):
"""Compile a PYX file to a DLL and return the name of the generated .so """Compile a PYX file to a DLL and return the name of the generated .so
or .dll .""" or .dll ."""
assert os.path.exists(filename), "Could not find %s" % os.path.abspath(filename) assert os.path.exists(filename), "Could not find %s" % os.path.abspath(filename)
...@@ -82,10 +83,16 @@ def pyx_to_dll(filename, ext = None, force_rebuild = 0, ...@@ -82,10 +83,16 @@ def pyx_to_dll(filename, ext = None, force_rebuild = 0,
try: try:
dist.run_commands()
obj_build_ext = dist.get_command_obj("build_ext") obj_build_ext = dist.get_command_obj("build_ext")
orig_inplace = obj_build_ext.inplace
if inplace:
obj_build_ext.inplace = True
try:
dist.run_commands()
finally:
obj_build_ext.inplace = orig_inplace
so_path = obj_build_ext.get_outputs()[0] so_path = obj_build_ext.get_outputs()[0]
if obj_build_ext.inplace: if orig_inplace or inplace:
# Python distutils get_outputs()[ returns a wrong so_path # Python distutils get_outputs()[ returns a wrong so_path
# when --inplace ; see http://bugs.python.org/issue5977 # when --inplace ; see http://bugs.python.org/issue5977
# workaround: # workaround:
......
...@@ -160,7 +160,7 @@ def handle_dependencies(pyxfilename): ...@@ -160,7 +160,7 @@ def handle_dependencies(pyxfilename):
if testing: if testing:
_test_files.append(file) _test_files.append(file)
def build_module(name, pyxfilename, pyxbuild_dir=None): def build_module(name, pyxfilename, pyxbuild_dir=None, inplace=False):
assert os.path.exists(pyxfilename), ( assert os.path.exists(pyxfilename), (
"Path does not exist: %s" % pyxfilename) "Path does not exist: %s" % pyxfilename)
handle_dependencies(pyxfilename) handle_dependencies(pyxfilename)
...@@ -176,6 +176,7 @@ def build_module(name, pyxfilename, pyxbuild_dir=None): ...@@ -176,6 +176,7 @@ def build_module(name, pyxfilename, pyxbuild_dir=None):
build_in_temp=build_in_temp, build_in_temp=build_in_temp,
pyxbuild_dir=pyxbuild_dir, pyxbuild_dir=pyxbuild_dir,
setup_args=sargs, setup_args=sargs,
inplace=inplace,
reload_support=pyxargs.reload_support) reload_support=pyxargs.reload_support)
assert os.path.exists(so_path), "Cannot find: %s" % so_path assert os.path.exists(so_path), "Cannot find: %s" % so_path
...@@ -190,13 +191,13 @@ def build_module(name, pyxfilename, pyxbuild_dir=None): ...@@ -190,13 +191,13 @@ def build_module(name, pyxfilename, pyxbuild_dir=None):
return so_path return so_path
def load_module(name, pyxfilename, pyxbuild_dir=None, is_package=False): def load_module(name, pyxfilename, pyxbuild_dir=None, is_package=False, build_inplace=False):
try: try:
if is_package: if is_package:
module_name = name + '.__init__' module_name = name + '.__init__'
else: else:
module_name = name module_name = name
so_path = build_module(module_name, pyxfilename, pyxbuild_dir) so_path = build_module(module_name, pyxfilename, pyxbuild_dir, inplace=build_inplace)
mod = imp.load_dynamic(name, so_path) mod = imp.load_dynamic(name, so_path)
assert mod.__file__ == so_path, (mod.__file__, so_path) assert mod.__file__ == so_path, (mod.__file__, so_path)
except Exception: except Exception:
...@@ -217,9 +218,10 @@ def load_module(name, pyxfilename, pyxbuild_dir=None, is_package=False): ...@@ -217,9 +218,10 @@ def load_module(name, pyxfilename, pyxbuild_dir=None, is_package=False):
class PyxImporter(object): class PyxImporter(object):
"""A meta-path importer for .pyx files. """A meta-path importer for .pyx files.
""" """
def __init__(self, extension=PYX_EXT, pyxbuild_dir=None): def __init__(self, extension=PYX_EXT, pyxbuild_dir=None, inplace=False):
self.extension = extension self.extension = extension
self.pyxbuild_dir = pyxbuild_dir self.pyxbuild_dir = pyxbuild_dir
self.inplace = inplace
def find_module(self, fullname, package_path=None): def find_module(self, fullname, package_path=None):
if fullname in sys.modules and not pyxargs.reload_support: if fullname in sys.modules and not pyxargs.reload_support:
...@@ -232,10 +234,12 @@ class PyxImporter(object): ...@@ -232,10 +234,12 @@ class PyxImporter(object):
if os.path.isfile(pkg_file): if os.path.isfile(pkg_file):
return PyxLoader(fullname, pathname, return PyxLoader(fullname, pathname,
init_path=pkg_file, init_path=pkg_file,
pyxbuild_dir=self.pyxbuild_dir) pyxbuild_dir=self.pyxbuild_dir,
inplace=self.inplace)
if pathname and pathname.endswith(self.extension): if pathname and pathname.endswith(self.extension):
return PyxLoader(fullname, pathname, return PyxLoader(fullname, pathname,
pyxbuild_dir=self.pyxbuild_dir) pyxbuild_dir=self.pyxbuild_dir,
inplace=self.inplace)
if ty != imp.C_EXTENSION: # only when an extension, check if we have a .pyx next! if ty != imp.C_EXTENSION: # only when an extension, check if we have a .pyx next!
return None return None
...@@ -243,8 +247,9 @@ class PyxImporter(object): ...@@ -243,8 +247,9 @@ class PyxImporter(object):
pyxpath = os.path.splitext(pathname)[0]+self.extension pyxpath = os.path.splitext(pathname)[0]+self.extension
if os.path.isfile(pyxpath): if os.path.isfile(pyxpath):
return PyxLoader(fullname, pyxpath, return PyxLoader(fullname, pyxpath,
pyxbuild_dir=self.pyxbuild_dir) pyxbuild_dir=self.pyxbuild_dir,
inplace=self.inplace)
# .so/.pyd's on PATH should not be remote from .pyx's # .so/.pyd's on PATH should not be remote from .pyx's
# think no need to implement PyxArgs.importer_search_remote here? # think no need to implement PyxArgs.importer_search_remote here?
...@@ -277,7 +282,8 @@ class PyxImporter(object): ...@@ -277,7 +282,8 @@ class PyxImporter(object):
path = os.getcwd() path = os.getcwd()
if is_file(path+sep+pyx_module_name): if is_file(path+sep+pyx_module_name):
return PyxLoader(fullname, join_path(path, pyx_module_name), return PyxLoader(fullname, join_path(path, pyx_module_name),
pyxbuild_dir=self.pyxbuild_dir) pyxbuild_dir=self.pyxbuild_dir,
inplace=self.inplace)
# not found, normal package, not a .pyx file, none of our business # not found, normal package, not a .pyx file, none of our business
_debug("%s not found" % fullname) _debug("%s not found" % fullname)
...@@ -286,9 +292,9 @@ class PyxImporter(object): ...@@ -286,9 +292,9 @@ class PyxImporter(object):
class PyImporter(PyxImporter): class PyImporter(PyxImporter):
"""A meta-path importer for normal .py files. """A meta-path importer for normal .py files.
""" """
def __init__(self, pyxbuild_dir=None): def __init__(self, pyxbuild_dir=None, inplace=False):
self.super = super(PyImporter, self) self.super = super(PyImporter, self)
self.super.__init__(extension='.py', pyxbuild_dir=pyxbuild_dir) self.super.__init__(extension='.py', pyxbuild_dir=pyxbuild_dir, inplace=inplace)
self.uncompilable_modules = {} self.uncompilable_modules = {}
self.blocked_modules = ['Cython', 'distutils.extension', self.blocked_modules = ['Cython', 'distutils.extension',
'distutils.sysconfig'] 'distutils.sysconfig']
...@@ -326,7 +332,8 @@ class PyImporter(PyxImporter): ...@@ -326,7 +332,8 @@ class PyImporter(PyxImporter):
real_name = fullname real_name = fullname
_debug("importer found path %s for module %s", path, real_name) _debug("importer found path %s for module %s", path, real_name)
build_module(real_name, path, build_module(real_name, path,
pyxbuild_dir=self.pyxbuild_dir) pyxbuild_dir=self.pyxbuild_dir,
inplace=self.inplace)
except Exception, e: except Exception, e:
if DEBUG_IMPORT: if DEBUG_IMPORT:
import traceback import traceback
...@@ -343,11 +350,12 @@ class PyImporter(PyxImporter): ...@@ -343,11 +350,12 @@ class PyImporter(PyxImporter):
return importer return importer
class PyxLoader(object): class PyxLoader(object):
def __init__(self, fullname, path, init_path=None, pyxbuild_dir=None): def __init__(self, fullname, path, init_path=None, pyxbuild_dir=None, inplace=False):
_debug("PyxLoader created for loading %s from %s (init path: %s)", fullname, path, init_path) _debug("PyxLoader created for loading %s from %s (init path: %s)", fullname, path, init_path)
self.fullname = fullname self.fullname = fullname
self.path, self.init_path = path, init_path self.path, self.init_path = path, init_path
self.pyxbuild_dir = pyxbuild_dir self.pyxbuild_dir = pyxbuild_dir
self.inplace=inplace
def load_module(self, fullname): def load_module(self, fullname):
assert self.fullname == fullname, ( assert self.fullname == fullname, (
...@@ -357,12 +365,14 @@ class PyxLoader(object): ...@@ -357,12 +365,14 @@ class PyxLoader(object):
# package # package
#print "PACKAGE", fullname #print "PACKAGE", fullname
module = load_module(fullname, self.init_path, module = load_module(fullname, self.init_path,
self.pyxbuild_dir, is_package=True) self.pyxbuild_dir, is_package=True,
build_inplace=self.inplace)
module.__path__ = [self.path] module.__path__ = [self.path]
else: else:
#print "MODULE", fullname #print "MODULE", fullname
module = load_module(fullname, self.path, module = load_module(fullname, self.path,
self.pyxbuild_dir) self.pyxbuild_dir,
build_inplace=self.inplace)
return module return module
...@@ -388,7 +398,7 @@ def _have_importers(): ...@@ -388,7 +398,7 @@ def _have_importers():
def install(pyximport=True, pyimport=False, build_dir=None, build_in_temp=True, def install(pyximport=True, pyimport=False, build_dir=None, build_in_temp=True,
setup_args={}, reload_support=False, setup_args={}, reload_support=False,
load_py_module_on_import_failure=False): load_py_module_on_import_failure=False, inplace=False):
"""Main entry point. Call this to install the .pyx import hook in """Main entry point. Call this to install the .pyx import hook in
your meta-path for a single Python process. If you want it to be your meta-path for a single Python process. If you want it to be
installed whenever you use Python, add it to your sitecustomize installed whenever you use Python, add it to your sitecustomize
...@@ -426,6 +436,8 @@ def install(pyximport=True, pyimport=False, build_dir=None, build_in_temp=True, ...@@ -426,6 +436,8 @@ def install(pyximport=True, pyimport=False, build_dir=None, build_in_temp=True,
the second import will rerun these modifications in whatever state the second import will rerun these modifications in whatever state
the system was left after the import of the compiled module the system was left after the import of the compiled module
failed. failed.
``inplace``: Install the compiled module next to the source file.
""" """
if not build_dir: if not build_dir:
build_dir = os.path.expanduser('~/.pyxbld') build_dir = os.path.expanduser('~/.pyxbld')
...@@ -442,11 +454,13 @@ def install(pyximport=True, pyimport=False, build_dir=None, build_in_temp=True, ...@@ -442,11 +454,13 @@ def install(pyximport=True, pyimport=False, build_dir=None, build_in_temp=True,
py_importer, pyx_importer = None, None py_importer, pyx_importer = None, None
if pyimport and not has_py_importer: if pyimport and not has_py_importer:
py_importer = PyImporter(pyxbuild_dir=build_dir) py_importer = PyImporter(pyxbuild_dir=build_dir, inplace=inplace)
# make sure we import Cython before we install the import hook
import pyxbuild, Cython.Compiler.Main, Cython.Compiler.Pipeline, Cython.Compiler.Optimize
sys.meta_path.insert(0, py_importer) sys.meta_path.insert(0, py_importer)
if pyximport and not has_pyx_importer: if pyximport and not has_pyx_importer:
pyx_importer = PyxImporter(pyxbuild_dir=build_dir) pyx_importer = PyxImporter(pyxbuild_dir=build_dir, inplace=inplace)
sys.meta_path.append(pyx_importer) sys.meta_path.append(pyx_importer)
return py_importer, pyx_importer return py_importer, pyx_importer
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment