[Zope-Checkins] CVS: Zope3/lib/python/Persistence - Function.py:1.5

Jeremy Hylton jeremy@zope.com
Mon, 24 Jun 2002 15:18:36 -0400


Update of /cvs-repository/Zope3/lib/python/Persistence
In directory cvs.zope.org:/tmp/cvs-serv31645

Modified Files:
	Function.py 
Log Message:
Try to cope with function that have module-level side-effects.



=== Zope3/lib/python/Persistence/Function.py 1.4 => 1.5 ===
 
+import dis
 import new
 import sys
 
 from Persistence import Persistent
 
+_STORE_GLOBAL = chr(dis.opname.index("STORE_GLOBAL"))
+
+def has_side_effect(func):
+    return _STORE_GLOBAL in func.func_code.co_code
+
 def get_code_args(co):
     """Return args from code object suitable for passing to constructor."""
     return (co.co_argcount,
@@ -32,6 +38,7 @@
         # base persistent getattr function does not unghostify an
         # object on refences to _p_ attributes.
         self._pf_func = func
+        self._v_side_effect = has_side_effect(func)
         self._pf_module = module
         self._pf_code = {}
 
@@ -54,6 +61,8 @@
             # it must be ours
             if attr.startswith('_pf_'):
                 self.__dict__[attr] = value
+                if attr == "_pf_func":
+                    self._v_side_effect = has_side_effect(self._pf_func)
             else:
                 setattr(self._pf_func, attr, value)
 
@@ -78,8 +87,19 @@
         # function is executing?  It seems like we need to expose
         # refcounts at the Python level to guarantee that this will
         # work.
-        
-        return self._pf_func(*args, **kwargs)
+
+        try:
+            return self._pf_func(*args, **kwargs)
+        finally:
+            # If the func has a side-effect, the module must be marked
+            # as changed.  We use the conservative approximation that
+            # any function with a STORE_GLOBAL opcode has a
+            # side-effect, regardless of whether a a particular call
+            # of the function actually executes STORE_GLOBAL.
+
+            # XXX Is this sufficient?
+            if self._v_side_effect:
+                self._pf_module._p_changed = 1
 
     def __getstate__(self):
         # If func_dict is empty, store None to avoid creating a dict
@@ -119,3 +139,4 @@
         if func_dict:
             func.func_dict.update(func_dict)
         self._pf_func = func
+        self._v_side_effect = has_side_effect(func)