Commits

Armin Rigo committed ab05b68

For interactive usage (playing around), add the option ffi.cdef("..",
override=True).

  • Participants
  • Parent commits f35564e

Comments (0)

Files changed (3)

         self._cached_btypes = {}
         self._parsed_types = new.module('parsed_types').__dict__
         self._new_types = new.module('new_types').__dict__
+        self._function_caches = []
         if hasattr(backend, 'set_ffi'):
             backend.set_ffi(self)
         #
         #
         self.NULL = self.cast("void *", 0)
 
-    def cdef(self, csource):
+    def cdef(self, csource, override=False):
         """Parse the given C source.  This registers all declared functions,
         types, and global variables.  The functions and global variables can
         then be accessed via either 'ffi.dlopen()' or 'ffi.verify()'.
         The types can be used in 'ffi.new()' and other functions.
         """
-        self._parser.parse(csource)
+        self._parser.parse(csource, override=override)
+        if override:
+            for cache in self._function_caches:
+                cache.clear()
 
     def dlopen(self, name):
         """Load and return a dynamic library identified by 'name'.
         library we only look for the actual (untyped) symbols.
         """
         assert isinstance(name, str) or name is None
-        return _make_ffi_library(self, name)
+        lib, function_cache = _make_ffi_library(self, name)
+        self._function_caches.append(function_cache)
+        return lib
 
     def typeof(self, cdecl, consider_function_as_funcptr=False):
         """Parse the C type given as a string and return the
     #
     if libname is not None:
         FFILibrary.__name__ = 'FFILibrary_%s' % libname
-    return FFILibrary()
+    return FFILibrary(), function_cache
         self._declarations = {}
         self._anonymous_counter = 0
         self._structnode2type = weakref.WeakKeyDictionary()
+        self._override = False
 
     def _parse(self, csource):
         # XXX: for more efficiency we would need to poke into the
         ast = _get_parser().parse(csource)
         return ast, macros
 
-    def parse(self, csource):
+    def parse(self, csource, override=False):
+        prev_override = self._override
+        try:
+            self._override = override
+            self._internal_parse(csource)
+        finally:
+            self._override = prev_override
+
+    def _internal_parse(self, csource):
         ast, macros = self._parse(csource)
         # add the macros
         for key, value in macros.items():
         if name in self._declarations:
             if self._declarations[name] is obj:
                 return
-            raise api.FFIError("multiple declarations of %s" % (name,))
+            if not self._override:
+                raise api.FFIError(
+                    "multiple declarations of %s (for interactive usage, "
+                    "try cdef(xx, override=True))" % (name,))
         assert name != '__dotdotdot__'
         self._declarations[name] = obj
 

testing/test_parsing.py

 import py, sys
-from cffi import FFI, CDefError, VerificationError
+from cffi import FFI, FFIError, CDefError, VerificationError
 
 class FakeBackend(object):
 
     assert repr(type_bar) == "<struct $1>"
     py.test.raises(VerificationError, type_bar.get_c_name)
     assert type_foo.get_c_name() == "foo_t"
+
+def test_override():
+    ffi = FFI(backend=FakeBackend())
+    C = ffi.dlopen(None)
+    ffi.cdef("int foo(void);")
+    py.test.raises(FFIError, ffi.cdef, "long foo(void);")
+    assert C.foo.BType == '<func (), <int>, False>'
+    ffi.cdef("long foo(void);", override=True)
+    assert C.foo.BType == '<func (), <long>, False>'