Commits

Jeremy Thurgood  committed ce63a7e

Experimental serialisation of cffi.model types into Python code that instantiates them.

  • Participants
  • Parent commits d18e79b
  • Branches split-verify

Comments (0)

Files changed (2)

File cffi/builder.py

 import os
-import pickle
 
 from .api import FFI
+from . import model
 
 
 MODULE_BOILERPLATE = """
 #####                                                                  #####
 
 import pickle
-from cffi import FFI
+from cffi import FFI, model
 
 
 _ffi = FFI()
 """
 
 
+class NotReadyYet(Exception):
+    pass
+
+
+class DeclarationBuilder(object):
+    def __init__(self, model_object, built_declarations, our_declarations):
+        self._model_object = model_object
+        self._built_declarations = built_declarations
+        self._our_declarations = our_declarations
+
+    def _format_param(self, param):
+        if isinstance(param, model.BaseTypeByIdentity):
+            od = (type(param), getattr(param, 'name', None))
+            if od not in self._our_declarations:
+                return DeclarationBuilder(
+                    param, self._built_declarations,
+                    self._our_declarations).build()
+            if param not in self._built_declarations:
+                raise NotReadyYet()
+            return "declarations[%r]" % self._built_declarations[param]
+        if isinstance(param, tuple):
+            return '(%s,)' % ', '.join(self._format_param(p) for p in param)
+        return repr(param)
+
+    def build(self):
+        try:
+            params = [(k, self._format_param(v))
+                      for k, v in self._model_object._get_items()]
+            if isinstance(self._model_object, model.StructOrUnion):
+                params.extend([
+                    ('fldnames', self._format_param(
+                        self._model_object.fldnames)),
+                    ('fldtypes', self._format_param(
+                        self._model_object.fldtypes)),
+                    ('fldbitsize', self._format_param(
+                        self._model_object.fldbitsize)),
+                ])
+            elif isinstance(self._model_object, model.EnumType):
+                params.extend([
+                    ('enumerators', self._format_param(
+                        self._model_object.enumerators)),
+                    ('enumvalues', self._format_param(
+                        self._model_object.enumvalues)),
+                    ('baseinttype', self._format_param(
+                        self._model_object.baseinttype)),
+                ])
+        except NotReadyYet:
+            return None
+
+        return "model.%s(%s)" % (
+            self._model_object.__class__.__name__, ', '.join(
+                '%s=%s' % param for param in params))
+
+
 class FFIBuilder(object):
     def __init__(self, module_name, build_path, backend=None):
         module_package = ''
         self.ffi.verifier.make_library(libfile_build_path)
         self._module_source += MAKELIB_FUNC_TEMPLATE % (libname, barefilename)
         self._built_files.append(libfile_path)
+        return self.ffi.verifier._load_library()
+
+    def _write_declarations(self):
+        self._module_source += "def _make_declarations():\n"
+        self._module_source += "    declarations = {}\n"
+
+        declarations = self.ffi._parser._declarations
+        our_decls = set((type(obj), getattr(obj, 'name', None))
+                        for obj in declarations.values())
+        built_decls = {}
+        decls = [(k, DeclarationBuilder(v, built_decls, our_decls))
+                 for k, v in self.ffi._parser._declarations.items()]
+
+        max_tries = (len(decls) + 1) ** 2 / 2
+
+        tries = 0
+        while decls:
+            tries += 1
+            if tries > max_tries:
+                raise Exception("Problem serialising declarations.")
+            name, dbuilder = decls.pop(0)
+            instantiation = dbuilder.build()
+            if instantiation is None:
+                decls.append((name, dbuilder))
+            else:
+                built_decls[dbuilder._model_object] = name
+                self._module_source += "    declarations[%r] = %s\n" % (
+                    name, instantiation)
+                if getattr(dbuilder._model_object, 'partial_resolved', None):
+                    self._module_source += (
+                        "    declarations[%r].partial = True\n" % (name,))
+                    self._module_source += (
+                        "    declarations[%r].partial_resolved = True\n" % (
+                            name,))
+
+        self._module_source += "    return declarations\n\n"
+        self._module_source += (
+            "_ffi._parser._declarations = _make_declarations()\n")
 
     def write_ffi_module(self):
-        self._module_source += (
-            "_ffi._parser._declarations = pickle.loads(%r)\n" %
-            pickle.dumps(self.ffi._parser._declarations, 2))
+        self._write_declarations()
         try:
             os.makedirs(self._build_path)
         except OSError:

File testing/test_makelib.py

 def test_ffi_do_some_stuff(tmpdir):
     builder = FFIBuilder("foo_ffi", str(tmpdir))
     builder.cdef("""
+        enum ee { EE1, EE2, EE3, ... };
         struct foo_s { int x; int y; };
         int grid_distance(struct foo_s offset);
     """)
     builder.makelib('foo', """
+        enum ee { EE1=10, EE2, EE3=-10, EE4 };
         struct foo_s { int x; int y; };
         int grid_distance(struct foo_s offset) {
             return offset.x + offset.y;
     assert foo_ffi.alignof('struct foo_s') == foo_ffi.sizeof('int')
     assert foo_ffi.typeof(foo_ffi.cast('long', 42)) == foo_ffi.typeof('long')
     assert foo_ffi.string(foo_ffi.new('char *', b"\x00")) == b""
+    assert foo_ffi.string(foo_ffi.cast('enum ee', 11)) == "EE2"
+    assert foo_ffi.string(foo_ffi.cast('enum ee', -10)) == "EE3"
 
     def cb(n):
         return n + 1