From aac0a994a2c314e0e639c5db3b7055dd8e3d2c87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20Calonne?= <aurel@nexedi.com>
Date: Tue, 25 Aug 2015 12:19:28 +0000
Subject: [PATCH] budget : move the class definition of cell from script to
 code

Using an instance of the class from the script create a huge memory leak
in zope which does not happen when instanciating class from the code.
The issue seems to come from the use by the matribox as simple test cases
of instanciation from script do not reproduce the memory leak
---
 .../BudgetLine_getAvailableBudgetCell.xml     | 84 -------------------
 .../BudgetLine_getConsumedBudgetCell.xml      | 84 -------------------
 .../BudgetLine_getEngagedBudgetCell.xml       | 84 -------------------
 .../matrixbox.xml                             |  2 +-
 .../matrixbox_quantity.xml                    |  2 +-
 .../matrixbox.xml                             |  2 +-
 .../matrixbox.xml                             |  2 +-
 product/ERP5/Document/BudgetLine.py           | 45 ++++++++++
 8 files changed, 49 insertions(+), 256 deletions(-)
 delete mode 100644 bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getAvailableBudgetCell.xml
 delete mode 100644 bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getConsumedBudgetCell.xml
 delete mode 100644 bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getEngagedBudgetCell.xml

diff --git a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getAvailableBudgetCell.xml b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getAvailableBudgetCell.xml
deleted file mode 100644
index a86684ccc8..0000000000
--- a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getAvailableBudgetCell.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0"?>
-<ZopeData>
-  <record id="1" aka="AAAAAAAAAAE=">
-    <pickle>
-      <global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
-    </pickle>
-    <pickle>
-      <dictionary>
-        <item>
-            <key> <string>Script_magic</string> </key>
-            <value> <int>3</int> </value>
-        </item>
-        <item>
-            <key> <string>_bind_names</string> </key>
-            <value>
-              <object>
-                <klass>
-                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
-                </klass>
-                <tuple/>
-                <state>
-                  <dictionary>
-                    <item>
-                        <key> <string>_asgns</string> </key>
-                        <value>
-                          <dictionary>
-                            <item>
-                                <key> <string>name_container</string> </key>
-                                <value> <string>container</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_context</string> </key>
-                                <value> <string>context</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_m_self</string> </key>
-                                <value> <string>script</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_subpath</string> </key>
-                                <value> <string>traverse_subpath</string> </value>
-                            </item>
-                          </dictionary>
-                        </value>
-                    </item>
-                  </dictionary>
-                </state>
-              </object>
-            </value>
-        </item>
-        <item>
-            <key> <string>_body</string> </key>
-            <value> <string>from ZTUtils import make_query\n
-\n
-def getFakeBudgetCell(amount):\n
-  class FakeBudgetCell:\n
-    def getAvailableBudget(self):\n
-      return amount\n
-    def getExplanationUrl(self, *args, **w):\n
-      return \'%s/BudgetLine_viewConsumedBudgetMovementList?%s\' % (\n
-                   context.absolute_url(),\n
-                   make_query(dict(cell_index=list(cell_index), engaged_budget=True)))\n
-  return FakeBudgetCell()\n
-\n
-try:\n
-  available_budget_dict = container.REQUEST.other[script.getId()]\n
-except KeyError:\n
-  available_budget_dict = container.REQUEST.other[script.getId()] = context.getAvailableBudgetDict()\n
-\n
-return getFakeBudgetCell(available_budget_dict.get(cell_index))\n
-</string> </value>
-        </item>
-        <item>
-            <key> <string>_params</string> </key>
-            <value> <string>*cell_index, **kw</string> </value>
-        </item>
-        <item>
-            <key> <string>id</string> </key>
-            <value> <string>BudgetLine_getAvailableBudgetCell</string> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>
diff --git a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getConsumedBudgetCell.xml b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getConsumedBudgetCell.xml
deleted file mode 100644
index 11c8878aa5..0000000000
--- a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getConsumedBudgetCell.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0"?>
-<ZopeData>
-  <record id="1" aka="AAAAAAAAAAE=">
-    <pickle>
-      <global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
-    </pickle>
-    <pickle>
-      <dictionary>
-        <item>
-            <key> <string>Script_magic</string> </key>
-            <value> <int>3</int> </value>
-        </item>
-        <item>
-            <key> <string>_bind_names</string> </key>
-            <value>
-              <object>
-                <klass>
-                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
-                </klass>
-                <tuple/>
-                <state>
-                  <dictionary>
-                    <item>
-                        <key> <string>_asgns</string> </key>
-                        <value>
-                          <dictionary>
-                            <item>
-                                <key> <string>name_container</string> </key>
-                                <value> <string>container</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_context</string> </key>
-                                <value> <string>context</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_m_self</string> </key>
-                                <value> <string>script</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_subpath</string> </key>
-                                <value> <string>traverse_subpath</string> </value>
-                            </item>
-                          </dictionary>
-                        </value>
-                    </item>
-                  </dictionary>
-                </state>
-              </object>
-            </value>
-        </item>
-        <item>
-            <key> <string>_body</string> </key>
-            <value> <string>from ZTUtils import make_query\n
-\n
-def getFakeBudgetCell(amount):\n
-  class FakeBudgetCell:\n
-    def getConsumedBudget(self):\n
-      return amount\n
-    def getExplanationUrl(self, *args, **w):\n
-      return \'%s/BudgetLine_viewConsumedBudgetMovementList?%s\' % (\n
-                   context.absolute_url(),\n
-                   make_query(dict(cell_index=list(cell_index), engaged_budget=False)))\n
-  return FakeBudgetCell()\n
-\n
-try:\n
-  consumed_budget_dict = container.REQUEST.other[script.getId()]\n
-except KeyError:\n
-  consumed_budget_dict = container.REQUEST.other[script.getId()] = context.getConsumedBudgetDict()\n
-\n
-return getFakeBudgetCell(consumed_budget_dict.get(cell_index))\n
-</string> </value>
-        </item>
-        <item>
-            <key> <string>_params</string> </key>
-            <value> <string>*cell_index, **kw</string> </value>
-        </item>
-        <item>
-            <key> <string>id</string> </key>
-            <value> <string>BudgetLine_getConsumedBudgetCell</string> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>
diff --git a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getEngagedBudgetCell.xml b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getEngagedBudgetCell.xml
deleted file mode 100644
index c431887c68..0000000000
--- a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_getEngagedBudgetCell.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0"?>
-<ZopeData>
-  <record id="1" aka="AAAAAAAAAAE=">
-    <pickle>
-      <global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
-    </pickle>
-    <pickle>
-      <dictionary>
-        <item>
-            <key> <string>Script_magic</string> </key>
-            <value> <int>3</int> </value>
-        </item>
-        <item>
-            <key> <string>_bind_names</string> </key>
-            <value>
-              <object>
-                <klass>
-                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
-                </klass>
-                <tuple/>
-                <state>
-                  <dictionary>
-                    <item>
-                        <key> <string>_asgns</string> </key>
-                        <value>
-                          <dictionary>
-                            <item>
-                                <key> <string>name_container</string> </key>
-                                <value> <string>container</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_context</string> </key>
-                                <value> <string>context</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_m_self</string> </key>
-                                <value> <string>script</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_subpath</string> </key>
-                                <value> <string>traverse_subpath</string> </value>
-                            </item>
-                          </dictionary>
-                        </value>
-                    </item>
-                  </dictionary>
-                </state>
-              </object>
-            </value>
-        </item>
-        <item>
-            <key> <string>_body</string> </key>
-            <value> <string>from ZTUtils import make_query\n
-\n
-def getFakeBudgetCell(amount):\n
-  class FakeBudgetCell:\n
-    def getEngagedBudget(self):\n
-      return amount\n
-    def getExplanationUrl(self, *args, **w):\n
-      return \'%s/BudgetLine_viewConsumedBudgetMovementList?%s\' % (\n
-                   context.absolute_url(),\n
-                   make_query(dict(cell_index=list(cell_index), engaged_budget=True)))\n
-  return FakeBudgetCell()\n
-\n
-try:\n
-  engaged_budget_dict = container.REQUEST.other[script.getId()]\n
-except KeyError:\n
-  engaged_budget_dict = container.REQUEST.other[script.getId()] = context.getEngagedBudgetDict()\n
-\n
-return getFakeBudgetCell(engaged_budget_dict.get(cell_index))\n
-</string> </value>
-        </item>
-        <item>
-            <key> <string>_params</string> </key>
-            <value> <string>*cell_index, **kw</string> </value>
-        </item>
-        <item>
-            <key> <string>id</string> </key>
-            <value> <string>BudgetLine_getEngagedBudgetCell</string> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>
diff --git a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox.xml b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox.xml
index 6b03dc46de..f3b8c3125d 100644
--- a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox.xml
+++ b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox.xml
@@ -81,7 +81,7 @@
                 </item>
                 <item>
                     <key> <string>cell_getter_method</string> </key>
-                    <value> <string>BudgetLine_getAvailableBudgetCell</string> </value>
+                    <value> <string>getAvailableBudgetCell</string> </value>
                 </item>
                 <item>
                     <key> <string>editable_attributes</string> </key>
diff --git a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox_quantity.xml b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox_quantity.xml
index 3c1c53e649..ac0fbb3516 100644
--- a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox_quantity.xml
+++ b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewAvailableBudget/matrixbox_quantity.xml
@@ -112,7 +112,7 @@
       <dictionary>
         <item>
             <key> <string>_text</string> </key>
-            <value> <string>python: cell.getAvailableBudget()</string> </value>
+            <value> <string>cell/getAvailableBudget</string> </value>
         </item>
       </dictionary>
     </pickle>
diff --git a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewConsumedBudget/matrixbox.xml b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewConsumedBudget/matrixbox.xml
index 7f6aaa8e87..9607d34045 100644
--- a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewConsumedBudget/matrixbox.xml
+++ b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewConsumedBudget/matrixbox.xml
@@ -81,7 +81,7 @@
                 </item>
                 <item>
                     <key> <string>cell_getter_method</string> </key>
-                    <value> <string>BudgetLine_getConsumedBudgetCell</string> </value>
+                    <value> <string>getConsumedBudgetCell</string> </value>
                 </item>
                 <item>
                     <key> <string>editable_attributes</string> </key>
diff --git a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewEngagedBudget/matrixbox.xml b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewEngagedBudget/matrixbox.xml
index c5b679f61a..f474c2192b 100644
--- a/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewEngagedBudget/matrixbox.xml
+++ b/bt5/erp5_budget/SkinTemplateItem/portal_skins/erp5_budget/BudgetLine_viewEngagedBudget/matrixbox.xml
@@ -81,7 +81,7 @@
                 </item>
                 <item>
                     <key> <string>cell_getter_method</string> </key>
-                    <value> <string>BudgetLine_getEngagedBudgetCell</string> </value>
+                    <value> <string>getEngagedBudgetCell</string> </value>
                 </item>
                 <item>
                     <key> <string>editable_attributes</string> </key>
diff --git a/product/ERP5/Document/BudgetLine.py b/product/ERP5/Document/BudgetLine.py
index 43f620a6c9..417a086510 100644
--- a/product/ERP5/Document/BudgetLine.py
+++ b/product/ERP5/Document/BudgetLine.py
@@ -34,6 +34,30 @@ from Products.ERP5Type.XMLMatrix import XMLMatrix
 from Products.ERP5Type.Core.Predicate import Predicate
 from Products.ERP5.mixin.variated import VariatedMixin
 from Products.ERP5Type.Cache import transactional_cached
+from ZTUtils import make_query
+
+class TempBudgetCell:
+  __slots__ = ('amount', 'cell_index', 'url', 'engaged_budget')
+  def __init__(self, amount, cell_index, url, engaged_budget):
+    self.amount = amount
+    self.cell_index = cell_index
+    self.url = url
+    self.engaged_budget = engaged_budget
+
+  def getConsumedBudget(self):
+    return self.amount
+
+  def getAvailableBudget(self):
+      return self.amount
+  
+  def getEngagedBudget(self):
+      return self.amount
+
+  def getExplanationUrl(self, *args, **w):
+    return '%s/BudgetLine_viewConsumedBudgetMovementList?%s' % (
+                 self.url,
+                 make_query(dict(cell_index=list(self.cell_index), engaged_budget=self.engaged_budget)))
+
 
 class BudgetLine(Predicate, XMLMatrix, VariatedMixin):
   """ A Line of budget, variated in budget cells.
@@ -58,6 +82,7 @@ class BudgetLine(Predicate, XMLMatrix, VariatedMixin):
   meta_type='ERP5 Budget Line'
   portal_type='Budget Line'
   add_permission = Permissions.AddPortalContent
+  __allow_access_to_unprotected_subobjects__ = 1
 
   # Declarative security
   security = ClassSecurityInfo()
@@ -136,3 +161,23 @@ class BudgetLine(Predicate, XMLMatrix, VariatedMixin):
 
     return budget_dict
 
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getConsumedBudgetCell')
+  def getConsumedBudgetCell(self, *cell_index, **kw):
+    consumed_budget_dict = self.getConsumedBudgetDict()
+    return TempBudgetCell(consumed_budget_dict.get(cell_index), 
+      cell_index, self.absolute_url(), False) 
+    
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getAvailableBudgetCell')
+  def getAvailableBudgetCell(self, *cell_index, **kw):
+    available_budget_dict = self.getAvailableBudgetDict()
+    return TempBudgetCell(available_budget_dict.get(cell_index), 
+      cell_index, self.absolute_url(), True) 
+
+  security.declareProtected(Permissions.AccessContentsInformation,
+                            'getEngagedBudgetCell')
+  def getEngagedBudgetCell(self, *cell_index, **kw):
+    engaged_budget_dict = self.getEngagedBudgetDict()
+    return TempBudgetCell(engaged_budget_dict.get(cell_index), 
+      cell_index, self.absolute_url(), True) 
-- 
2.30.9