github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/packaging/ci/lambda/GitPullS3/cffi/verifier.py (about) 1 # 2 # DEPRECATED: implementation for ffi.verify() 3 # 4 import sys, os, binascii, shutil, io 5 from . import __version_verifier_modules__ 6 from . import ffiplatform 7 8 if sys.version_info >= (3, 3): 9 import importlib.machinery 10 def _extension_suffixes(): 11 return importlib.machinery.EXTENSION_SUFFIXES[:] 12 else: 13 import imp 14 def _extension_suffixes(): 15 return [suffix for suffix, _, type in imp.get_suffixes() 16 if type == imp.C_EXTENSION] 17 18 19 if sys.version_info >= (3,): 20 NativeIO = io.StringIO 21 else: 22 class NativeIO(io.BytesIO): 23 def write(self, s): 24 if isinstance(s, unicode): 25 s = s.encode('ascii') 26 super(NativeIO, self).write(s) 27 28 def _hack_at_distutils(): 29 # Windows-only workaround for some configurations: see 30 # https://bugs.python.org/issue23246 (Python 2.7 with 31 # a specific MS compiler suite download) 32 if sys.platform == "win32": 33 try: 34 import setuptools # for side-effects, patches distutils 35 except ImportError: 36 pass 37 38 39 class Verifier(object): 40 41 def __init__(self, ffi, preamble, tmpdir=None, modulename=None, 42 ext_package=None, tag='', force_generic_engine=False, 43 source_extension='.c', flags=None, relative_to=None, **kwds): 44 if ffi._parser._uses_new_feature: 45 raise ffiplatform.VerificationError( 46 "feature not supported with ffi.verify(), but only " 47 "with ffi.set_source(): %s" % (ffi._parser._uses_new_feature,)) 48 self.ffi = ffi 49 self.preamble = preamble 50 if not modulename: 51 flattened_kwds = ffiplatform.flatten(kwds) 52 vengine_class = _locate_engine_class(ffi, force_generic_engine) 53 self._vengine = vengine_class(self) 54 self._vengine.patch_extension_kwds(kwds) 55 self.flags = flags 56 self.kwds = self.make_relative_to(kwds, relative_to) 57 # 58 if modulename: 59 if tag: 60 raise TypeError("can't specify both 'modulename' and 'tag'") 61 else: 62 key = '\x00'.join([sys.version[:3], __version_verifier_modules__, 63 preamble, flattened_kwds] + 64 ffi._cdefsources) 65 if sys.version_info >= (3,): 66 key = key.encode('utf-8') 67 k1 = hex(binascii.crc32(key[0::2]) & 0xffffffff) 68 k1 = k1.lstrip('0x').rstrip('L') 69 k2 = hex(binascii.crc32(key[1::2]) & 0xffffffff) 70 k2 = k2.lstrip('0').rstrip('L') 71 modulename = '_cffi_%s_%s%s%s' % (tag, self._vengine._class_key, 72 k1, k2) 73 suffix = _get_so_suffixes()[0] 74 self.tmpdir = tmpdir or _caller_dir_pycache() 75 self.sourcefilename = os.path.join(self.tmpdir, modulename + source_extension) 76 self.modulefilename = os.path.join(self.tmpdir, modulename + suffix) 77 self.ext_package = ext_package 78 self._has_source = False 79 self._has_module = False 80 81 def write_source(self, file=None): 82 """Write the C source code. It is produced in 'self.sourcefilename', 83 which can be tweaked beforehand.""" 84 with self.ffi._lock: 85 if self._has_source and file is None: 86 raise ffiplatform.VerificationError( 87 "source code already written") 88 self._write_source(file) 89 90 def compile_module(self): 91 """Write the C source code (if not done already) and compile it. 92 This produces a dynamic link library in 'self.modulefilename'.""" 93 with self.ffi._lock: 94 if self._has_module: 95 raise ffiplatform.VerificationError("module already compiled") 96 if not self._has_source: 97 self._write_source() 98 self._compile_module() 99 100 def load_library(self): 101 """Get a C module from this Verifier instance. 102 Returns an instance of a FFILibrary class that behaves like the 103 objects returned by ffi.dlopen(), but that delegates all 104 operations to the C module. If necessary, the C code is written 105 and compiled first. 106 """ 107 with self.ffi._lock: 108 if not self._has_module: 109 self._locate_module() 110 if not self._has_module: 111 if not self._has_source: 112 self._write_source() 113 self._compile_module() 114 return self._load_library() 115 116 def get_module_name(self): 117 basename = os.path.basename(self.modulefilename) 118 # kill both the .so extension and the other .'s, as introduced 119 # by Python 3: 'basename.cpython-33m.so' 120 basename = basename.split('.', 1)[0] 121 # and the _d added in Python 2 debug builds --- but try to be 122 # conservative and not kill a legitimate _d 123 if basename.endswith('_d') and hasattr(sys, 'gettotalrefcount'): 124 basename = basename[:-2] 125 return basename 126 127 def get_extension(self): 128 _hack_at_distutils() # backward compatibility hack 129 if not self._has_source: 130 with self.ffi._lock: 131 if not self._has_source: 132 self._write_source() 133 sourcename = ffiplatform.maybe_relative_path(self.sourcefilename) 134 modname = self.get_module_name() 135 return ffiplatform.get_extension(sourcename, modname, **self.kwds) 136 137 def generates_python_module(self): 138 return self._vengine._gen_python_module 139 140 def make_relative_to(self, kwds, relative_to): 141 if relative_to and os.path.dirname(relative_to): 142 dirname = os.path.dirname(relative_to) 143 kwds = kwds.copy() 144 for key in ffiplatform.LIST_OF_FILE_NAMES: 145 if key in kwds: 146 lst = kwds[key] 147 if not isinstance(lst, (list, tuple)): 148 raise TypeError("keyword '%s' should be a list or tuple" 149 % (key,)) 150 lst = [os.path.join(dirname, fn) for fn in lst] 151 kwds[key] = lst 152 return kwds 153 154 # ---------- 155 156 def _locate_module(self): 157 if not os.path.isfile(self.modulefilename): 158 if self.ext_package: 159 try: 160 pkg = __import__(self.ext_package, None, None, ['__doc__']) 161 except ImportError: 162 return # cannot import the package itself, give up 163 # (e.g. it might be called differently before installation) 164 path = pkg.__path__ 165 else: 166 path = None 167 filename = self._vengine.find_module(self.get_module_name(), path, 168 _get_so_suffixes()) 169 if filename is None: 170 return 171 self.modulefilename = filename 172 self._vengine.collect_types() 173 self._has_module = True 174 175 def _write_source_to(self, file): 176 self._vengine._f = file 177 try: 178 self._vengine.write_source_to_f() 179 finally: 180 del self._vengine._f 181 182 def _write_source(self, file=None): 183 if file is not None: 184 self._write_source_to(file) 185 else: 186 # Write our source file to an in memory file. 187 f = NativeIO() 188 self._write_source_to(f) 189 source_data = f.getvalue() 190 191 # Determine if this matches the current file 192 if os.path.exists(self.sourcefilename): 193 with open(self.sourcefilename, "r") as fp: 194 needs_written = not (fp.read() == source_data) 195 else: 196 needs_written = True 197 198 # Actually write the file out if it doesn't match 199 if needs_written: 200 _ensure_dir(self.sourcefilename) 201 with open(self.sourcefilename, "w") as fp: 202 fp.write(source_data) 203 204 # Set this flag 205 self._has_source = True 206 207 def _compile_module(self): 208 # compile this C source 209 tmpdir = os.path.dirname(self.sourcefilename) 210 outputfilename = ffiplatform.compile(tmpdir, self.get_extension()) 211 try: 212 same = ffiplatform.samefile(outputfilename, self.modulefilename) 213 except OSError: 214 same = False 215 if not same: 216 _ensure_dir(self.modulefilename) 217 shutil.move(outputfilename, self.modulefilename) 218 self._has_module = True 219 220 def _load_library(self): 221 assert self._has_module 222 if self.flags is not None: 223 return self._vengine.load_library(self.flags) 224 else: 225 return self._vengine.load_library() 226 227 # ____________________________________________________________ 228 229 _FORCE_GENERIC_ENGINE = False # for tests 230 231 def _locate_engine_class(ffi, force_generic_engine): 232 if _FORCE_GENERIC_ENGINE: 233 force_generic_engine = True 234 if not force_generic_engine: 235 if '__pypy__' in sys.builtin_module_names: 236 force_generic_engine = True 237 else: 238 try: 239 import _cffi_backend 240 except ImportError: 241 _cffi_backend = '?' 242 if ffi._backend is not _cffi_backend: 243 force_generic_engine = True 244 if force_generic_engine: 245 from . import vengine_gen 246 return vengine_gen.VGenericEngine 247 else: 248 from . import vengine_cpy 249 return vengine_cpy.VCPythonEngine 250 251 # ____________________________________________________________ 252 253 _TMPDIR = None 254 255 def _caller_dir_pycache(): 256 if _TMPDIR: 257 return _TMPDIR 258 result = os.environ.get('CFFI_TMPDIR') 259 if result: 260 return result 261 filename = sys._getframe(2).f_code.co_filename 262 return os.path.abspath(os.path.join(os.path.dirname(filename), 263 '__pycache__')) 264 265 def set_tmpdir(dirname): 266 """Set the temporary directory to use instead of __pycache__.""" 267 global _TMPDIR 268 _TMPDIR = dirname 269 270 def cleanup_tmpdir(tmpdir=None, keep_so=False): 271 """Clean up the temporary directory by removing all files in it 272 called `_cffi_*.{c,so}` as well as the `build` subdirectory.""" 273 tmpdir = tmpdir or _caller_dir_pycache() 274 try: 275 filelist = os.listdir(tmpdir) 276 except OSError: 277 return 278 if keep_so: 279 suffix = '.c' # only remove .c files 280 else: 281 suffix = _get_so_suffixes()[0].lower() 282 for fn in filelist: 283 if fn.lower().startswith('_cffi_') and ( 284 fn.lower().endswith(suffix) or fn.lower().endswith('.c')): 285 try: 286 os.unlink(os.path.join(tmpdir, fn)) 287 except OSError: 288 pass 289 clean_dir = [os.path.join(tmpdir, 'build')] 290 for dir in clean_dir: 291 try: 292 for fn in os.listdir(dir): 293 fn = os.path.join(dir, fn) 294 if os.path.isdir(fn): 295 clean_dir.append(fn) 296 else: 297 os.unlink(fn) 298 except OSError: 299 pass 300 301 def _get_so_suffixes(): 302 suffixes = _extension_suffixes() 303 if not suffixes: 304 # bah, no C_EXTENSION available. Occurs on pypy without cpyext 305 if sys.platform == 'win32': 306 suffixes = [".pyd"] 307 else: 308 suffixes = [".so"] 309 310 return suffixes 311 312 def _ensure_dir(filename): 313 try: 314 os.makedirs(os.path.dirname(filename)) 315 except OSError: 316 pass