codekingpro/portable-devtools
114k
1#2# DEPRECATED: implementation for ffi.verify()3#4import sys, os, binascii, shutil, io5from . import __version_verifier_modules__6from . import ffiplatform7from .error import VerificationError8 9if sys.version_info >= (3, 3):10 import importlib.machinery11 def _extension_suffixes():12 return importlib.machinery.EXTENSION_SUFFIXES[:]13else:14 import imp15 def _extension_suffixes():16 return [suffix for suffix, _, type in imp.get_suffixes()17 if type == imp.C_EXTENSION]18 19 20if sys.version_info >= (3,):21 NativeIO = io.StringIO22else:23 class NativeIO(io.BytesIO):24 def write(self, s):25 if isinstance(s, unicode):26 s = s.encode('ascii')27 super(NativeIO, self).write(s)28 29 30class Verifier(object):31 32 def __init__(self, ffi, preamble, tmpdir=None, modulename=None,33 ext_package=None, tag='', force_generic_engine=False,34 source_extension='.c', flags=None, relative_to=None, **kwds):35 if ffi._parser._uses_new_feature:36 raise VerificationError(37 "feature not supported with ffi.verify(), but only "38 "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,))39 self.ffi = ffi40 self.preamble = preamble41 if not modulename:42 flattened_kwds = ffiplatform.flatten(kwds)43 vengine_class = _locate_engine_class(ffi, force_generic_engine)44 self._vengine = vengine_class(self)45 self._vengine.patch_extension_kwds(kwds)46 self.flags = flags47 self.kwds = self.make_relative_to(kwds, relative_to)48 #49 if modulename:50 if tag:51 raise TypeError("can't specify both 'modulename' and 'tag'")52 else:53 key = '\x00'.join(['%d.%d' % sys.version_info[:2],54 __version_verifier_modules__,55 preamble, flattened_kwds] +56 ffi._cdefsources)57 if sys.version_info >= (3,):58 key = key.encode('utf-8')59 k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff)60 k1 = k1.lstrip('0x').rstrip('L')61 k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff)62 k2 = k2.lstrip('0').rstrip('L')63 modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key,64 k1, k2)65 suffix = _get_so_suffixes()[0]66 self.tmpdir = tmpdir or _caller_dir_pycache()67 self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension)68 self.modulefilename = os.path.join(self.tmpdir, modulename + suffix)69 self.ext_package = ext_package70 self._has_source = False71 self._has_module = False72 73 def write_source(self, file=None):74 """Write the C source code. It is produced in 'self.sourcefilename',75 which can be tweaked beforehand."""76 with self.ffi._lock:77 if self._has_source and file is None:78 raise VerificationError(79 "source code already written")80 self._write_source(file)81 82 def compile_module(self):83 """Write the C source code (if not done already) and compile it.84 This produces a dynamic link library in 'self.modulefilename'."""85 with self.ffi._lock:86 if self._has_module:87 raise VerificationError("module already compiled")88 if not self._has_source:89 self._write_source()90 self._compile_module()91 92 def load_library(self):93 """Get a C module from this Verifier instance.94 Returns an instance of a FFILibrary class that behaves like the95 objects returned by ffi.dlopen(), but that delegates all96 operations to the C module. If necessary, the C code is written97 and compiled first.98 """99 with self.ffi._lock:100 if not self._has_module:101 self._locate_module()102 if not self._has_module:103 if not self._has_source:104 self._write_source()105 self._compile_module()106 return self._load_library()107 108 def get_module_name(self):109 basename = os.path.basename(self.modulefilename)110 # kill both the .so extension and the other .'s, as introduced111 # by Python 3: 'basename.cpython-33m.so'112 basename = basename.split('.', 1)[0]113 # and the _d added in Python 2 debug builds --- but try to be114 # conservative and not kill a legitimate _d115 if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'):116 basename = basename[:-2]117 return basename118 119 def get_extension(self):120 if not self._has_source:121 with self.ffi._lock:122 if not self._has_source:123 self._write_source()124 sourcename = ffiplatform.maybe_relative_path(self.sourcefilename)125 modname = self.get_module_name()126 return ffiplatform.get_extension(sourcename, modname, **self.kwds)127 128 def generates_python_module(self):129 return self._vengine._gen_python_module130 131 def make_relative_to(self, kwds, relative_to):132 if relative_to and os.path.dirname(relative_to):133 dirname = os.path.dirname(relative_to)134 kwds = kwds.copy()135 for key in ffiplatform.LIST_OF_FILE_NAMES:136 if key in kwds:137 lst = kwds[key]138 if not isinstance(lst, (list, tuple)):139 raise TypeError("keyword '%s' should be a list or tuple"140 % (key,))141 lst = [os.path.join(dirname, fn) for fn in lst]142 kwds[key] = lst143 return kwds144 145 # ----------146 147 def _locate_module(self):148 if not os.path.isfile(self.modulefilename):149 if self.ext_package:150 try:151 pkg = __import__(self.ext_package, None, None, ['__doc__'])152 except ImportError:153 return # cannot import the package itself, give up154 # (e.g. it might be called differently before installation)155 path = pkg.__path__156 else:157 path = None158 filename = self._vengine.find_module(self.get_module_name(), path,159 _get_so_suffixes())160 if filename is None:161 return162 self.modulefilename = filename163 self._vengine.collect_types()164 self._has_module = True165 166 def _write_source_to(self, file):167 self._vengine._f = file168 try:169 self._vengine.write_source_to_f()170 finally:171 del self._vengine._f172 173 def _write_source(self, file=None):174 if file is not None:175 self._write_source_to(file)176 else:177 # Write our source file to an in memory file.178 f = NativeIO()179 self._write_source_to(f)180 source_data = f.getvalue()181 182 # Determine if this matches the current file183 if os.path.exists(self.sourcefilename):184 with open(self.sourcefilename, "r") as fp:185 needs_written = not (fp.read() == source_data)186 else:187 needs_written = True188 189 # Actually write the file out if it doesn't match190 if needs_written:191 _ensure_dir(self.sourcefilename)192 with open(self.sourcefilename, "w") as fp:193 fp.write(source_data)194 195 # Set this flag196 self._has_source = True197 198 def _compile_module(self):199 # compile this C source200 tmpdir = os.path.dirname(self.sourcefilename)201 outputfilename = ffiplatform.compile(tmpdir, self.get_extension())202 try:203 same = ffiplatform.samefile(outputfilename, self.modulefilename)204 except OSError:205 same = False206 if not same:207 _ensure_dir(self.modulefilename)208 shutil.move(outputfilename, self.modulefilename)209 self._has_module = True210 211 def _load_library(self):212 assert self._has_module213 if self.flags is not None:214 return self._vengine.load_library(self.flags)215 else:216 return self._vengine.load_library()217 218# ____________________________________________________________219 220_FORCE_GENERIC_ENGINE = False # for tests221 222def _locate_engine_class(ffi, force_generic_engine):223 if _FORCE_GENERIC_ENGINE:224 force_generic_engine = True225 if not force_generic_engine:226 if '__pypy__' in sys.builtin_module_names:227 force_generic_engine = True228 else:229 try:230 import _cffi_backend231 except ImportError:232 _cffi_backend = '?'233 if ffi._backend is not _cffi_backend:234 force_generic_engine = True235 if force_generic_engine:236 from . import vengine_gen237 return vengine_gen.VGenericEngine238 else:239 from . import vengine_cpy240 return vengine_cpy.VCPythonEngine241 242# ____________________________________________________________243 244_TMPDIR = None245 246def _caller_dir_pycache():247 if _TMPDIR:248 return _TMPDIR249 result = os.environ.get('CFFI_TMPDIR')250 if result:251 return result252 filename = sys._getframe(2).f_code.co_filename253 return os.path.abspath(os.path.join(os.path.dirname(filename),254 '__pycache__'))255 256def set_tmpdir(dirname):257 """Set the temporary directory to use instead of __pycache__."""258 global _TMPDIR259 _TMPDIR = dirname260 261def cleanup_tmpdir(tmpdir=None, keep_so=False):262 """Clean up the temporary directory by removing all files in it263 called `_cffi_*.{c,so}` as well as the `build` subdirectory."""264 tmpdir = tmpdir or _caller_dir_pycache()265 try:266 filelist = os.listdir(tmpdir)267 except OSError:268 return269 if keep_so:270 suffix = '.c' # only remove .c files271 else:272 suffix = _get_so_suffixes()[0].lower()273 for fn in filelist:274 if fn.lower().startswith('_cffi_') and (275 fn.lower().endswith(suffix) or fn.lower().endswith('.c')):276 try:277 os.unlink(os.path.join(tmpdir, fn))278 except OSError:279 pass280 clean_dir = [os.path.join(tmpdir, 'build')]281 for dir in clean_dir:282 try:283 for fn in os.listdir(dir):284 fn = os.path.join(dir, fn)285 if os.path.isdir(fn):286 clean_dir.append(fn)287 else:288 os.unlink(fn)289 except OSError:290 pass291 292def _get_so_suffixes():293 suffixes = _extension_suffixes()294 if not suffixes:295 # bah, no C_EXTENSION available. Occurs on pypy without cpyext296 if sys.platform == 'win32':297 suffixes = [".pyd"]298 else:299 suffixes = [".so"]300 301 return suffixes302 303def _ensure_dir(filename):304 dirname = os.path.dirname(filename)305 if dirname and not os.path.isdir(dirname):306 os.makedirs(dirname)307 