OLD | NEW |
(Empty) | |
| 1 """Build a Pyrex file from .pyx source to .so loadable module using |
| 2 the installed distutils infrastructure. Call: |
| 3 |
| 4 out_fname = pyx_to_dll("foo.pyx") |
| 5 """ |
| 6 import os |
| 7 import sys |
| 8 |
| 9 from distutils.dist import Distribution |
| 10 from distutils.errors import DistutilsArgError, DistutilsError, CCompilerError |
| 11 from distutils.extension import Extension |
| 12 from distutils.util import grok_environment_error |
| 13 try: |
| 14 from Cython.Distutils import build_ext |
| 15 HAS_CYTHON = True |
| 16 except ImportError: |
| 17 HAS_CYTHON = False |
| 18 |
| 19 DEBUG = 0 |
| 20 |
| 21 _reloads={} |
| 22 |
| 23 def pyx_to_dll(filename, ext = None, force_rebuild = 0, |
| 24 build_in_temp=False, pyxbuild_dir=None, setup_args={}, |
| 25 reload_support=False, inplace=False): |
| 26 """Compile a PYX file to a DLL and return the name of the generated .so |
| 27 or .dll .""" |
| 28 assert os.path.exists(filename), "Could not find %s" % os.path.abspath(filen
ame) |
| 29 |
| 30 path, name = os.path.split(os.path.abspath(filename)) |
| 31 |
| 32 if not ext: |
| 33 modname, extension = os.path.splitext(name) |
| 34 assert extension in (".pyx", ".py"), extension |
| 35 if not HAS_CYTHON: |
| 36 filename = filename[:-len(extension)] + '.c' |
| 37 ext = Extension(name=modname, sources=[filename]) |
| 38 |
| 39 if not pyxbuild_dir: |
| 40 pyxbuild_dir = os.path.join(path, "_pyxbld") |
| 41 |
| 42 package_base_dir = path |
| 43 for package_name in ext.name.split('.')[-2::-1]: |
| 44 package_base_dir, pname = os.path.split(package_base_dir) |
| 45 if pname != package_name: |
| 46 # something is wrong - package path doesn't match file path |
| 47 package_base_dir = None |
| 48 break |
| 49 |
| 50 script_args=setup_args.get("script_args",[]) |
| 51 if DEBUG or "--verbose" in script_args: |
| 52 quiet = "--verbose" |
| 53 else: |
| 54 quiet = "--quiet" |
| 55 args = [quiet, "build_ext"] |
| 56 if force_rebuild: |
| 57 args.append("--force") |
| 58 if inplace and package_base_dir: |
| 59 args.extend(['--build-lib', package_base_dir]) |
| 60 if ext.name == '__init__' or ext.name.endswith('.__init__'): |
| 61 # package => provide __path__ early |
| 62 if not hasattr(ext, 'cython_directives'): |
| 63 ext.cython_directives = {'set_initial_path' : 'SOURCEFILE'} |
| 64 elif 'set_initial_path' not in ext.cython_directives: |
| 65 ext.cython_directives['set_initial_path'] = 'SOURCEFILE' |
| 66 |
| 67 if HAS_CYTHON and build_in_temp: |
| 68 args.append("--pyrex-c-in-temp") |
| 69 sargs = setup_args.copy() |
| 70 sargs.update( |
| 71 {"script_name": None, |
| 72 "script_args": args + script_args} ) |
| 73 dist = Distribution(sargs) |
| 74 if not dist.ext_modules: |
| 75 dist.ext_modules = [] |
| 76 dist.ext_modules.append(ext) |
| 77 if HAS_CYTHON: |
| 78 dist.cmdclass = {'build_ext': build_ext} |
| 79 build = dist.get_command_obj('build') |
| 80 build.build_base = pyxbuild_dir |
| 81 |
| 82 config_files = dist.find_config_files() |
| 83 try: config_files.remove('setup.cfg') |
| 84 except ValueError: pass |
| 85 dist.parse_config_files(config_files) |
| 86 |
| 87 cfgfiles = dist.find_config_files() |
| 88 try: cfgfiles.remove('setup.cfg') |
| 89 except ValueError: pass |
| 90 dist.parse_config_files(cfgfiles) |
| 91 try: |
| 92 ok = dist.parse_command_line() |
| 93 except DistutilsArgError: |
| 94 raise |
| 95 |
| 96 if DEBUG: |
| 97 print("options (after parsing command line):") |
| 98 dist.dump_option_dicts() |
| 99 assert ok |
| 100 |
| 101 |
| 102 try: |
| 103 obj_build_ext = dist.get_command_obj("build_ext") |
| 104 dist.run_commands() |
| 105 so_path = obj_build_ext.get_outputs()[0] |
| 106 if obj_build_ext.inplace: |
| 107 # Python distutils get_outputs()[ returns a wrong so_path |
| 108 # when --inplace ; see http://bugs.python.org/issue5977 |
| 109 # workaround: |
| 110 so_path = os.path.join(os.path.dirname(filename), |
| 111 os.path.basename(so_path)) |
| 112 if reload_support: |
| 113 org_path = so_path |
| 114 timestamp = os.path.getmtime(org_path) |
| 115 global _reloads |
| 116 last_timestamp, last_path, count = _reloads.get(org_path, (None,None
,0) ) |
| 117 if last_timestamp == timestamp: |
| 118 so_path = last_path |
| 119 else: |
| 120 basename = os.path.basename(org_path) |
| 121 while count < 100: |
| 122 count += 1 |
| 123 r_path = os.path.join(obj_build_ext.build_lib, |
| 124 basename + '.reload%s'%count) |
| 125 try: |
| 126 import shutil # late import / reload_support is: debuggi
ng |
| 127 try: |
| 128 # Try to unlink first --- if the .so file |
| 129 # is mmapped by another process, |
| 130 # overwriting its contents corrupts the |
| 131 # loaded image (on Linux) and crashes the |
| 132 # other process. On Windows, unlinking an |
| 133 # open file just fails. |
| 134 if os.path.isfile(r_path): |
| 135 os.unlink(r_path) |
| 136 except OSError: |
| 137 continue |
| 138 shutil.copy2(org_path, r_path) |
| 139 so_path = r_path |
| 140 except IOError: |
| 141 continue |
| 142 break |
| 143 else: |
| 144 # used up all 100 slots |
| 145 raise ImportError("reload count for %s reached maximum"%org_
path) |
| 146 _reloads[org_path]=(timestamp, so_path, count) |
| 147 return so_path |
| 148 except KeyboardInterrupt: |
| 149 sys.exit(1) |
| 150 except (IOError, os.error): |
| 151 exc = sys.exc_info()[1] |
| 152 error = grok_environment_error(exc) |
| 153 |
| 154 if DEBUG: |
| 155 sys.stderr.write(error + "\n") |
| 156 raise |
| 157 |
| 158 if __name__=="__main__": |
| 159 pyx_to_dll("dummy.pyx") |
| 160 import test |
| 161 |
OLD | NEW |