| Index: third_party/cython/src/pyximport/pyximport.py
|
| diff --git a/third_party/cython/src/pyximport/pyximport.py b/third_party/cython/src/pyximport/pyximport.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4fd7fe90e70b060d19a2675399d6267ba4ac66d2
|
| --- /dev/null
|
| +++ b/third_party/cython/src/pyximport/pyximport.py
|
| @@ -0,0 +1,559 @@
|
| +"""
|
| +Import hooks; when installed with the install() function, these hooks
|
| +allow importing .pyx files as if they were Python modules.
|
| +
|
| +If you want the hook installed every time you run Python
|
| +you can add it to your Python version by adding these lines to
|
| +sitecustomize.py (which you can create from scratch in site-packages
|
| +if it doesn't exist there or somewhere else on your python path)::
|
| +
|
| + import pyximport
|
| + pyximport.install()
|
| +
|
| +For instance on the Mac with a non-system Python 2.3, you could create
|
| +sitecustomize.py with only those two lines at
|
| +/usr/local/lib/python2.3/site-packages/sitecustomize.py .
|
| +
|
| +A custom distutils.core.Extension instance and setup() args
|
| +(Distribution) for for the build can be defined by a <modulename>.pyxbld
|
| +file like:
|
| +
|
| +# examplemod.pyxbld
|
| +def make_ext(modname, pyxfilename):
|
| + from distutils.extension import Extension
|
| + return Extension(name = modname,
|
| + sources=[pyxfilename, 'hello.c'],
|
| + include_dirs=['/myinclude'] )
|
| +def make_setup_args():
|
| + return dict(script_args=["--compiler=mingw32"])
|
| +
|
| +Extra dependencies can be defined by a <modulename>.pyxdep .
|
| +See README.
|
| +
|
| +Since Cython 0.11, the :mod:`pyximport` module also has experimental
|
| +compilation support for normal Python modules. This allows you to
|
| +automatically run Cython on every .pyx and .py module that Python
|
| +imports, including parts of the standard library and installed
|
| +packages. Cython will still fail to compile a lot of Python modules,
|
| +in which case the import mechanism will fall back to loading the
|
| +Python source modules instead. The .py import mechanism is installed
|
| +like this::
|
| +
|
| + pyximport.install(pyimport = True)
|
| +
|
| +Running this module as a top-level script will run a test and then print
|
| +the documentation.
|
| +
|
| +This code is based on the Py2.3+ import protocol as described in PEP 302.
|
| +"""
|
| +
|
| +import sys
|
| +import os
|
| +import glob
|
| +import imp
|
| +
|
| +mod_name = "pyximport"
|
| +
|
| +assert sys.hexversion >= 0x2030000, "need Python 2.3 or later"
|
| +
|
| +PYX_EXT = ".pyx"
|
| +PYXDEP_EXT = ".pyxdep"
|
| +PYXBLD_EXT = ".pyxbld"
|
| +
|
| +DEBUG_IMPORT = False
|
| +
|
| +def _print(message, args):
|
| + if args:
|
| + message = message % args
|
| + print(message)
|
| +
|
| +def _debug(message, *args):
|
| + if DEBUG_IMPORT:
|
| + _print(message, args)
|
| +
|
| +def _info(message, *args):
|
| + _print(message, args)
|
| +
|
| +# Performance problem: for every PYX file that is imported, we will
|
| +# invoke the whole distutils infrastructure even if the module is
|
| +# already built. It might be more efficient to only do it when the
|
| +# mod time of the .pyx is newer than the mod time of the .so but
|
| +# the question is how to get distutils to tell me the name of the .so
|
| +# before it builds it. Maybe it is easy...but maybe the peformance
|
| +# issue isn't real.
|
| +def _load_pyrex(name, filename):
|
| + "Load a pyrex file given a name and filename."
|
| +
|
| +def get_distutils_extension(modname, pyxfilename, language_level=None):
|
| +# try:
|
| +# import hashlib
|
| +# except ImportError:
|
| +# import md5 as hashlib
|
| +# extra = "_" + hashlib.md5(open(pyxfilename).read()).hexdigest()
|
| +# modname = modname + extra
|
| + extension_mod,setup_args = handle_special_build(modname, pyxfilename)
|
| + if not extension_mod:
|
| + if not isinstance(pyxfilename, str):
|
| + # distutils is stupid in Py2 and requires exactly 'str'
|
| + # => encode accidentally coerced unicode strings back to str
|
| + pyxfilename = pyxfilename.encode(sys.getfilesystemencoding())
|
| + from distutils.extension import Extension
|
| + extension_mod = Extension(name = modname, sources=[pyxfilename])
|
| + if language_level is not None:
|
| + extension_mod.cython_directives = {'language_level': language_level}
|
| + return extension_mod,setup_args
|
| +
|
| +def handle_special_build(modname, pyxfilename):
|
| + special_build = os.path.splitext(pyxfilename)[0] + PYXBLD_EXT
|
| + ext = None
|
| + setup_args={}
|
| + if os.path.exists(special_build):
|
| + # globls = {}
|
| + # locs = {}
|
| + # execfile(special_build, globls, locs)
|
| + # ext = locs["make_ext"](modname, pyxfilename)
|
| + mod = imp.load_source("XXXX", special_build, open(special_build))
|
| + make_ext = getattr(mod,'make_ext',None)
|
| + if make_ext:
|
| + ext = make_ext(modname, pyxfilename)
|
| + assert ext and ext.sources, ("make_ext in %s did not return Extension"
|
| + % special_build)
|
| + make_setup_args = getattr(mod,'make_setup_args',None)
|
| + if make_setup_args:
|
| + setup_args = make_setup_args()
|
| + assert isinstance(setup_args,dict), ("make_setup_args in %s did not return a dict"
|
| + % special_build)
|
| + assert set or setup_args, ("neither make_ext nor make_setup_args %s"
|
| + % special_build)
|
| + ext.sources = [os.path.join(os.path.dirname(special_build), source)
|
| + for source in ext.sources]
|
| + return ext, setup_args
|
| +
|
| +def handle_dependencies(pyxfilename):
|
| + testing = '_test_files' in globals()
|
| + dependfile = os.path.splitext(pyxfilename)[0] + PYXDEP_EXT
|
| +
|
| + # by default let distutils decide whether to rebuild on its own
|
| + # (it has a better idea of what the output file will be)
|
| +
|
| + # but we know more about dependencies so force a rebuild if
|
| + # some of the dependencies are newer than the pyxfile.
|
| + if os.path.exists(dependfile):
|
| + depends = open(dependfile).readlines()
|
| + depends = [depend.strip() for depend in depends]
|
| +
|
| + # gather dependencies in the "files" variable
|
| + # the dependency file is itself a dependency
|
| + files = [dependfile]
|
| + for depend in depends:
|
| + fullpath = os.path.join(os.path.dirname(dependfile),
|
| + depend)
|
| + files.extend(glob.glob(fullpath))
|
| +
|
| + # only for unit testing to see we did the right thing
|
| + if testing:
|
| + _test_files[:] = [] #$pycheck_no
|
| +
|
| + # if any file that the pyxfile depends upon is newer than
|
| + # the pyx file, 'touch' the pyx file so that distutils will
|
| + # be tricked into rebuilding it.
|
| + for file in files:
|
| + from distutils.dep_util import newer
|
| + if newer(file, pyxfilename):
|
| + _debug("Rebuilding %s because of %s", pyxfilename, file)
|
| + filetime = os.path.getmtime(file)
|
| + os.utime(pyxfilename, (filetime, filetime))
|
| + if testing:
|
| + _test_files.append(file)
|
| +
|
| +def build_module(name, pyxfilename, pyxbuild_dir=None, inplace=False, language_level=None):
|
| + assert os.path.exists(pyxfilename), (
|
| + "Path does not exist: %s" % pyxfilename)
|
| + handle_dependencies(pyxfilename)
|
| +
|
| + extension_mod,setup_args = get_distutils_extension(name, pyxfilename, language_level)
|
| + build_in_temp=pyxargs.build_in_temp
|
| + sargs=pyxargs.setup_args.copy()
|
| + sargs.update(setup_args)
|
| + build_in_temp=sargs.pop('build_in_temp',build_in_temp)
|
| +
|
| + import pyxbuild
|
| + so_path = pyxbuild.pyx_to_dll(pyxfilename, extension_mod,
|
| + build_in_temp=build_in_temp,
|
| + pyxbuild_dir=pyxbuild_dir,
|
| + setup_args=sargs,
|
| + inplace=inplace,
|
| + reload_support=pyxargs.reload_support)
|
| + assert os.path.exists(so_path), "Cannot find: %s" % so_path
|
| +
|
| + junkpath = os.path.join(os.path.dirname(so_path), name+"_*") #very dangerous with --inplace ? yes, indeed, trying to eat my files ;)
|
| + junkstuff = glob.glob(junkpath)
|
| + for path in junkstuff:
|
| + if path!=so_path:
|
| + try:
|
| + os.remove(path)
|
| + except IOError:
|
| + _info("Couldn't remove %s", path)
|
| +
|
| + return so_path
|
| +
|
| +def load_module(name, pyxfilename, pyxbuild_dir=None, is_package=False,
|
| + build_inplace=False, language_level=None, so_path=None):
|
| + try:
|
| + if so_path is None:
|
| + if is_package:
|
| + module_name = name + '.__init__'
|
| + else:
|
| + module_name = name
|
| + so_path = build_module(module_name, pyxfilename, pyxbuild_dir,
|
| + inplace=build_inplace, language_level=language_level)
|
| + mod = imp.load_dynamic(name, so_path)
|
| + if is_package and not hasattr(mod, '__path__'):
|
| + mod.__path__ = [os.path.dirname(so_path)]
|
| + assert mod.__file__ == so_path, (mod.__file__, so_path)
|
| + except Exception:
|
| + if pyxargs.load_py_module_on_import_failure and pyxfilename.endswith('.py'):
|
| + # try to fall back to normal import
|
| + mod = imp.load_source(name, pyxfilename)
|
| + assert mod.__file__ in (pyxfilename, pyxfilename+'c', pyxfilename+'o'), (mod.__file__, pyxfilename)
|
| + else:
|
| + import traceback
|
| + raise ImportError("Building module %s failed: %s" %
|
| + (name,
|
| + traceback.format_exception_only(*sys.exc_info()[:2]))), None, sys.exc_info()[2]
|
| + return mod
|
| +
|
| +
|
| +# import hooks
|
| +
|
| +class PyxImporter(object):
|
| + """A meta-path importer for .pyx files.
|
| + """
|
| + def __init__(self, extension=PYX_EXT, pyxbuild_dir=None, inplace=False,
|
| + language_level=None):
|
| + self.extension = extension
|
| + self.pyxbuild_dir = pyxbuild_dir
|
| + self.inplace = inplace
|
| + self.language_level = language_level
|
| +
|
| + def find_module(self, fullname, package_path=None):
|
| + if fullname in sys.modules and not pyxargs.reload_support:
|
| + return None # only here when reload()
|
| + try:
|
| + fp, pathname, (ext,mode,ty) = imp.find_module(fullname,package_path)
|
| + if fp: fp.close() # Python should offer a Default-Loader to avoid this double find/open!
|
| + if pathname and ty == imp.PKG_DIRECTORY:
|
| + pkg_file = os.path.join(pathname, '__init__'+self.extension)
|
| + if os.path.isfile(pkg_file):
|
| + return PyxLoader(fullname, pathname,
|
| + init_path=pkg_file,
|
| + pyxbuild_dir=self.pyxbuild_dir,
|
| + inplace=self.inplace,
|
| + language_level=self.language_level)
|
| + if pathname and pathname.endswith(self.extension):
|
| + return PyxLoader(fullname, pathname,
|
| + pyxbuild_dir=self.pyxbuild_dir,
|
| + inplace=self.inplace,
|
| + language_level=self.language_level)
|
| + if ty != imp.C_EXTENSION: # only when an extension, check if we have a .pyx next!
|
| + return None
|
| +
|
| + # find .pyx fast, when .so/.pyd exist --inplace
|
| + pyxpath = os.path.splitext(pathname)[0]+self.extension
|
| + if os.path.isfile(pyxpath):
|
| + return PyxLoader(fullname, pyxpath,
|
| + pyxbuild_dir=self.pyxbuild_dir,
|
| + inplace=self.inplace,
|
| + language_level=self.language_level)
|
| +
|
| + # .so/.pyd's on PATH should not be remote from .pyx's
|
| + # think no need to implement PyxArgs.importer_search_remote here?
|
| +
|
| + except ImportError:
|
| + pass
|
| +
|
| + # searching sys.path ...
|
| +
|
| + #if DEBUG_IMPORT: print "SEARCHING", fullname, package_path
|
| + if '.' in fullname: # only when package_path anyway?
|
| + mod_parts = fullname.split('.')
|
| + module_name = mod_parts[-1]
|
| + else:
|
| + module_name = fullname
|
| + pyx_module_name = module_name + self.extension
|
| + # this may work, but it returns the file content, not its path
|
| + #import pkgutil
|
| + #pyx_source = pkgutil.get_data(package, pyx_module_name)
|
| +
|
| + if package_path:
|
| + paths = package_path
|
| + else:
|
| + paths = sys.path
|
| + join_path = os.path.join
|
| + is_file = os.path.isfile
|
| + is_abs = os.path.isabs
|
| + abspath = os.path.abspath
|
| + #is_dir = os.path.isdir
|
| + sep = os.path.sep
|
| + for path in paths:
|
| + if not path:
|
| + path = os.getcwd()
|
| + elif not is_abs(path):
|
| + path = abspath(path)
|
| + if is_file(path+sep+pyx_module_name):
|
| + return PyxLoader(fullname, join_path(path, pyx_module_name),
|
| + pyxbuild_dir=self.pyxbuild_dir,
|
| + inplace=self.inplace,
|
| + language_level=self.language_level)
|
| +
|
| + # not found, normal package, not a .pyx file, none of our business
|
| + _debug("%s not found" % fullname)
|
| + return None
|
| +
|
| +class PyImporter(PyxImporter):
|
| + """A meta-path importer for normal .py files.
|
| + """
|
| + def __init__(self, pyxbuild_dir=None, inplace=False, language_level=None):
|
| + if language_level is None:
|
| + language_level = sys.version_info[0]
|
| + self.super = super(PyImporter, self)
|
| + self.super.__init__(extension='.py', pyxbuild_dir=pyxbuild_dir, inplace=inplace,
|
| + language_level=language_level)
|
| + self.uncompilable_modules = {}
|
| + self.blocked_modules = ['Cython', 'pyxbuild', 'pyximport.pyxbuild',
|
| + 'distutils.extension', 'distutils.sysconfig']
|
| +
|
| + def find_module(self, fullname, package_path=None):
|
| + if fullname in sys.modules:
|
| + return None
|
| + if fullname.startswith('Cython.'):
|
| + return None
|
| + if fullname in self.blocked_modules:
|
| + # prevent infinite recursion
|
| + return None
|
| + if _lib_loader.knows(fullname):
|
| + return _lib_loader
|
| + _debug("trying import of module '%s'", fullname)
|
| + if fullname in self.uncompilable_modules:
|
| + path, last_modified = self.uncompilable_modules[fullname]
|
| + try:
|
| + new_last_modified = os.stat(path).st_mtime
|
| + if new_last_modified > last_modified:
|
| + # import would fail again
|
| + return None
|
| + except OSError:
|
| + # module is no longer where we found it, retry the import
|
| + pass
|
| +
|
| + self.blocked_modules.append(fullname)
|
| + try:
|
| + importer = self.super.find_module(fullname, package_path)
|
| + if importer is not None:
|
| + if importer.init_path:
|
| + path = importer.init_path
|
| + real_name = fullname + '.__init__'
|
| + else:
|
| + path = importer.path
|
| + real_name = fullname
|
| + _debug("importer found path %s for module %s", path, real_name)
|
| + try:
|
| + so_path = build_module(
|
| + real_name, path,
|
| + pyxbuild_dir=self.pyxbuild_dir,
|
| + language_level=self.language_level,
|
| + inplace=self.inplace)
|
| + _lib_loader.add_lib(fullname, path, so_path,
|
| + is_package=bool(importer.init_path))
|
| + return _lib_loader
|
| + except Exception:
|
| + if DEBUG_IMPORT:
|
| + import traceback
|
| + traceback.print_exc()
|
| + # build failed, not a compilable Python module
|
| + try:
|
| + last_modified = os.stat(path).st_mtime
|
| + except OSError:
|
| + last_modified = 0
|
| + self.uncompilable_modules[fullname] = (path, last_modified)
|
| + importer = None
|
| + finally:
|
| + self.blocked_modules.pop()
|
| + return importer
|
| +
|
| +class LibLoader(object):
|
| + def __init__(self):
|
| + self._libs = {}
|
| +
|
| + def load_module(self, fullname):
|
| + try:
|
| + source_path, so_path, is_package = self._libs[fullname]
|
| + except KeyError:
|
| + raise ValueError("invalid module %s" % fullname)
|
| + _debug("Loading shared library module '%s' from %s", fullname, so_path)
|
| + return load_module(fullname, source_path, so_path=so_path, is_package=is_package)
|
| +
|
| + def add_lib(self, fullname, path, so_path, is_package):
|
| + self._libs[fullname] = (path, so_path, is_package)
|
| +
|
| + def knows(self, fullname):
|
| + return fullname in self._libs
|
| +
|
| +_lib_loader = LibLoader()
|
| +
|
| +class PyxLoader(object):
|
| + def __init__(self, fullname, path, init_path=None, pyxbuild_dir=None,
|
| + inplace=False, language_level=None):
|
| + _debug("PyxLoader created for loading %s from %s (init path: %s)",
|
| + fullname, path, init_path)
|
| + self.fullname = fullname
|
| + self.path, self.init_path = path, init_path
|
| + self.pyxbuild_dir = pyxbuild_dir
|
| + self.inplace = inplace
|
| + self.language_level = language_level
|
| +
|
| + def load_module(self, fullname):
|
| + assert self.fullname == fullname, (
|
| + "invalid module, expected %s, got %s" % (
|
| + self.fullname, fullname))
|
| + if self.init_path:
|
| + # package
|
| + #print "PACKAGE", fullname
|
| + module = load_module(fullname, self.init_path,
|
| + self.pyxbuild_dir, is_package=True,
|
| + build_inplace=self.inplace,
|
| + language_level=self.language_level)
|
| + module.__path__ = [self.path]
|
| + else:
|
| + #print "MODULE", fullname
|
| + module = load_module(fullname, self.path,
|
| + self.pyxbuild_dir,
|
| + build_inplace=self.inplace,
|
| + language_level=self.language_level)
|
| + return module
|
| +
|
| +
|
| +#install args
|
| +class PyxArgs(object):
|
| + build_dir=True
|
| + build_in_temp=True
|
| + setup_args={} #None
|
| +
|
| +##pyxargs=None
|
| +
|
| +def _have_importers():
|
| + has_py_importer = False
|
| + has_pyx_importer = False
|
| + for importer in sys.meta_path:
|
| + if isinstance(importer, PyxImporter):
|
| + if isinstance(importer, PyImporter):
|
| + has_py_importer = True
|
| + else:
|
| + has_pyx_importer = True
|
| +
|
| + return has_py_importer, has_pyx_importer
|
| +
|
| +def install(pyximport=True, pyimport=False, build_dir=None, build_in_temp=True,
|
| + setup_args={}, reload_support=False,
|
| + load_py_module_on_import_failure=False, inplace=False,
|
| + language_level=None):
|
| + """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
|
| + installed whenever you use Python, add it to your sitecustomize
|
| + (as described above).
|
| +
|
| + You can pass ``pyimport=True`` to also install the .py import hook
|
| + in your meta-path. Note, however, that it is highly experimental,
|
| + will not work for most .py files, and will therefore only slow
|
| + down your imports. Use at your own risk.
|
| +
|
| + By default, compiled modules will end up in a ``.pyxbld``
|
| + directory in the user's home directory. Passing a different path
|
| + as ``build_dir`` will override this.
|
| +
|
| + ``build_in_temp=False`` will produce the C files locally. Working
|
| + with complex dependencies and debugging becomes more easy. This
|
| + can principally interfere with existing files of the same name.
|
| + build_in_temp can be overriden by <modulename>.pyxbld/make_setup_args()
|
| + by a dict item of 'build_in_temp'
|
| +
|
| + ``setup_args``: dict of arguments for Distribution - see
|
| + distutils.core.setup() . They are extended/overriden by those of
|
| + <modulename>.pyxbld/make_setup_args()
|
| +
|
| + ``reload_support``: Enables support for dynamic
|
| + reload(<pyxmodulename>), e.g. after a change in the Cython code.
|
| + Additional files <so_path>.reloadNN may arise on that account, when
|
| + the previously loaded module file cannot be overwritten.
|
| +
|
| + ``load_py_module_on_import_failure``: If the compilation of a .py
|
| + file succeeds, but the subsequent import fails for some reason,
|
| + retry the import with the normal .py module instead of the
|
| + compiled module. Note that this may lead to unpredictable results
|
| + for modules that change the system state during their import, as
|
| + the second import will rerun these modifications in whatever state
|
| + the system was left after the import of the compiled module
|
| + failed.
|
| +
|
| + ``inplace``: Install the compiled module next to the source file.
|
| +
|
| + ``language_level``: The source language level to use: 2 or 3.
|
| + The default is to use the language level of the current Python
|
| + runtime for .py files and Py2 for .pyx files.
|
| + """
|
| + if not build_dir:
|
| + build_dir = os.path.join(os.path.expanduser('~'), '.pyxbld')
|
| +
|
| + global pyxargs
|
| + pyxargs = PyxArgs() #$pycheck_no
|
| + pyxargs.build_dir = build_dir
|
| + pyxargs.build_in_temp = build_in_temp
|
| + pyxargs.setup_args = (setup_args or {}).copy()
|
| + pyxargs.reload_support = reload_support
|
| + pyxargs.load_py_module_on_import_failure = load_py_module_on_import_failure
|
| +
|
| + has_py_importer, has_pyx_importer = _have_importers()
|
| + py_importer, pyx_importer = None, None
|
| +
|
| + if pyimport and not has_py_importer:
|
| + py_importer = PyImporter(pyxbuild_dir=build_dir, inplace=inplace,
|
| + language_level=language_level)
|
| + # make sure we import Cython before we install the import hook
|
| + import Cython.Compiler.Main, Cython.Compiler.Pipeline, Cython.Compiler.Optimize
|
| + sys.meta_path.insert(0, py_importer)
|
| +
|
| + if pyximport and not has_pyx_importer:
|
| + pyx_importer = PyxImporter(pyxbuild_dir=build_dir, inplace=inplace,
|
| + language_level=language_level)
|
| + sys.meta_path.append(pyx_importer)
|
| +
|
| + return py_importer, pyx_importer
|
| +
|
| +def uninstall(py_importer, pyx_importer):
|
| + """
|
| + Uninstall an import hook.
|
| + """
|
| + try:
|
| + sys.meta_path.remove(py_importer)
|
| + except ValueError:
|
| + pass
|
| +
|
| + try:
|
| + sys.meta_path.remove(pyx_importer)
|
| + except ValueError:
|
| + pass
|
| +
|
| +# MAIN
|
| +
|
| +def show_docs():
|
| + import __main__
|
| + __main__.__name__ = mod_name
|
| + for name in dir(__main__):
|
| + item = getattr(__main__, name)
|
| + try:
|
| + setattr(item, "__module__", mod_name)
|
| + except (AttributeError, TypeError):
|
| + pass
|
| + help(__main__)
|
| +
|
| +if __name__ == '__main__':
|
| + show_docs()
|
|
|