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