From 13b0fb9a5680f948f8fcefea99c8500fbbec924b Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@nexedi.com>
Date: Sun, 17 Jan 2010 22:20:23 +0000
Subject: [PATCH] Fix support of BT containing instances of classes defined by
 the BT itself.

This fixes [28422] and testBPMEvaluation (PicklingError exceptions).
This reverts useless [30411].
It also allows to revert [31213].

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@31791 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/BusinessTemplate.py  | 25 +++++++++++++++++-----
 product/ERP5Type/tests/ERP5TypeTestCase.py |  3 +--
 2 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/product/ERP5/Document/BusinessTemplate.py b/product/ERP5/Document/BusinessTemplate.py
index 8f967b25bf..07d4a70935 100644
--- a/product/ERP5/Document/BusinessTemplate.py
+++ b/product/ERP5/Document/BusinessTemplate.py
@@ -5611,15 +5611,22 @@ Business Template is a set of definitions, such as skins, portal types and categ
       # Create temporary modules/classes for classes defined by this BT.
       # This is required if the BT contains instances of one of these classes.
       module_id_list = []
+      instance_oid_list = []
       for template_type in ('Constraint', 'Document', 'PropertySheet'):
         for template_id in getattr(self,
                                    'getTemplate%sIdList' % template_type)():
           module_id = 'Products.ERP5Type.%s.%s' % (template_type, template_id)
-          if module_id not in sys.modules:
-            module_id_list.append(module_id)
-            sys.modules[module_id] = module = imp.new_module(module_id)
-            module.SimpleItem = SimpleItem.SimpleItem
-            exec "class %s(SimpleItem): pass" % template_id in module.__dict__
+          module_id_list.append(module_id)
+          # Always redefine the module, so that 'instance_oid_list' contains
+          # the full list of oid to remove from pickle cache.
+          sys.modules[module_id] = module = imp.new_module(module_id)
+          module.SimpleItem = SimpleItem.SimpleItem
+          module.instance_oid_list = instance_oid_list
+          exec """class %s(SimpleItem):
+            def __setstate__(self, state):
+              instance_oid_list.append(self._p_oid)
+              return SimpleItem.__setstate__(self, state)""" % template_id \
+            in module.__dict__
 
       for item_name in self._item_name_list:
         getattr(self, item_name).importFile(bta)
@@ -5628,6 +5635,14 @@ Business Template is a set of definitions, such as skins, portal types and categ
       # (during the installation).
       for module_id in module_id_list:
         del sys.modules[module_id]
+      # Remove from pickle cache all objects whose classes are temporary
+      # (= defined by this BT). This forces to reload these objects on next
+      # access, with the correct classes.
+      # Invalidation is forced using __delitem__ instead of invalidate.
+      if instance_oid_list:
+        pickle_cache = self.getPortalObject()._p_jar._cache
+        for oid in instance_oid_list:
+          del pickle_cache[oid]
 
     def getItemsList(self):
       """Return list of items in business template
diff --git a/product/ERP5Type/tests/ERP5TypeTestCase.py b/product/ERP5Type/tests/ERP5TypeTestCase.py
index 1aa8c14a84..004defcfc1 100644
--- a/product/ERP5Type/tests/ERP5TypeTestCase.py
+++ b/product/ERP5Type/tests/ERP5TypeTestCase.py
@@ -11,7 +11,7 @@ import base64
 import errno
 import md5
 import os
-#import random # XXX
+import random
 import re
 import socket
 import sys
@@ -753,7 +753,6 @@ class ERP5TypeTestCase(backportUnittest.TestCase, PortalTestCase):
       """Starts an HTTP ZServer thread."""
       from Testing.ZopeTestCase import threadutils, utils
       if utils._Z2HOST is None:
-        import random
         randint = random.Random(hash(os.environ['INSTANCE_HOME'])).randint
         def zserverRunner():
           try:
-- 
2.30.9