Commit 04f331a2 authored by Vincent Pelletier's avatar Vincent Pelletier

PythonScript, SQLMethod: Fix expression_instance getting stale

Before this change, expression_instance would not be set when expression is
modified, leading to either non-applied filter expression (when no
expression_instance existed) or use of an outdated expression.
Use a volatile attribute to store the expression: it does not change the
amount of work needed to load the Expression instance (pickle is the raw
python expression as text) and it remove data duplication in exported
documents.
parent ede919f6
......@@ -33,6 +33,7 @@ from App.special_dtml import HTMLFile
from Products.ERP5Type.XMLObject import XMLObject
from Products.PythonScripts.PythonScript import \
PythonScript as ZopePythonScript
from Products.ERP5.mixin.expression import ExpressionMixin
# Only needed until skin tool is migrated
manage_addPythonScriptFormThroughZMI = \
......@@ -63,7 +64,7 @@ class PythonScriptThroughZMI(XMLObject):
def __init__(self, *args, **kw):
assert False
class PythonScript(XMLObject, ZopePythonScript):
class PythonScript(XMLObject, ZopePythonScript, ExpressionMixin):
""" Script python for ERP5
"""
......
......@@ -35,6 +35,7 @@ from Products.ERP5Type.XMLObject import XMLObject
from Products.PythonScripts.PythonScript import \
PythonScript as ZopePythonScript
from Products.ZSQLMethods.SQL import SQL as ZSQL
from Products.ERP5.mixin.expression import ExpressionMixin
# New ZSQLMethod addition function
def manage_addSQLMethod(self, id, title='',
......@@ -59,7 +60,7 @@ def manage_addSQLMethod(self, id, title='',
REQUEST['RESPONSE'].redirect( 'manage_main' )
return c
class SQLMethod(XMLObject, ZSQL):
class SQLMethod(XMLObject, ZSQL, ExpressionMixin):
"""SQLMethod for ERP5.
"""
......
......@@ -64,12 +64,6 @@
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -95,17 +89,4 @@
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.isResourceType()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -50,12 +50,6 @@
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -89,17 +83,4 @@
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: getattr(context, \'getLanguage\', None) is not None or getattr(context, \'getVersion\', None) is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -51,12 +51,6 @@ getAlarmDate</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -90,17 +84,4 @@ getAlarmDate</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.isAlarmType()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -62,12 +62,6 @@ getStopDateRangeMax</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -101,17 +95,4 @@ getStopDateRangeMax</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.isDelivery()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -60,12 +60,6 @@ getMovedItemUidList</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -99,17 +93,4 @@ getMovedItemUidList</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.providesIMovement()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -51,12 +51,6 @@ getMeasureRowList</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -90,17 +84,4 @@ getMeasureRowList</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.isResourceType()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -51,12 +51,6 @@ getCategoryList</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -90,17 +84,4 @@ getCategoryList</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.providesIMovement()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -51,12 +51,6 @@ getAcquiredCategoryList</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -90,17 +84,4 @@ getAcquiredCategoryList</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: not context.providesIMovement()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -53,12 +53,6 @@ getObject</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -92,17 +86,4 @@ getObject</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.isPredicate()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -52,12 +52,6 @@ predicate_property_dict</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -91,17 +85,4 @@ predicate_property_dict</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.isPredicate()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -28,12 +28,6 @@ getQuantityUnitConversionDefinitionRowList\r\n
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -59,17 +53,4 @@ getQuantityUnitConversionDefinitionRowList\r\n
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.isResourceType()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -81,12 +81,6 @@ asMovementList</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -120,17 +114,4 @@ asMovementList</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: context.providesIMovement() and not context.isInventoryMovement()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -51,12 +51,6 @@ optimised_subject_list</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -90,17 +84,4 @@ optimised_subject_list</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: getattr(context, \'getLanguage\', None) is not None or getattr(context, \'getVersion\', None) is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -51,12 +51,6 @@ getUserId</string> </value>
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -90,17 +84,4 @@ getUserId</string> </value>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: \'ERP5User\' in getattr(getattr(context.getPortalObject().portal_types, context.getPortalType(), None), \'getTypePropertySheetList\', lambda: ())()</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
......@@ -65,12 +65,6 @@ getFrequencyIndex\r\n
</tuple>
</value>
</item>
<item>
<key> <string>expression_instance</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
<item>
<key> <string>filtered</string> </key>
<value> <int>1</int> </value>
......@@ -104,17 +98,4 @@ getFrequencyIndex\r\n
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="Expression" module="Products.CMFCore.Expression"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>text</string> </key>
<value> <string>python: getattr(context, \'getLanguage\', None) is not None or getattr(context, \'getVersion\', None) is not None</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
##############################################################################
#
# Copyright (c) 2018 Nexedi SA and Contributors. All Rights Reserved.
# Vincent Pelletier <vincent@nexedi.com>
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsibility of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# guarantees and support are strongly adviced to contract a Free Software
# Service Company
#
# This program is Free Software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
from AccessControl import ClassSecurityInfo
from Products.CMFCore.Expression import Expression
from Products.ERP5Type import Permissions
from Products.ERP5Type.Globals import InitializeClass
class ExpressionMixin:
security = ClassSecurityInfo()
def _setExpression(self, value):
try:
del self._v_expression_instance
except AttributeError:
pass
self._baseSetExpression(value)
security.declareProtected(Permissions.AccessContentsInformation, 'getExpressionInstance')
def getExpressionInstance(self, default=None):
try:
return self._v_expression_instance
except AttributeError:
result = Expression(self.getExpression())
self._v_expression_instance = result
return result
InitializeClass(ExpressionMixin)
......@@ -39,7 +39,6 @@ from App.config import getConfiguration
from Products.ERP5Type.tests.Sequence import SequenceList, Sequence
from urllib import pathname2url
from Products.ERP5Type.Globals import PersistentMapping
from Products.CMFCore.Expression import Expression
from Products.ERP5Type.dynamic.lazy_class import ERP5BaseBroken
from Products.ERP5Type.tests.utils import LogInterceptor
from Products.ERP5Type.Workflow import addWorkflowByType
......@@ -1600,10 +1599,8 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor):
catalog.sql_uncatalog_object = tuple(sql_uncatalog_object)
# set filter for this method
expression = 'python: context.isPredicate()'
expr_instance = Expression(expression)
zsql_method.setFiltered(1)
zsql_method.setExpression(expression)
zsql_method.setExpressionInstance(expr_instance)
zsql_method.setExpressionCacheKey('portal_type')
zsql_method.setTypeList([])
......@@ -1625,10 +1622,8 @@ class BusinessTemplateMixin(ERP5TypeTestCase, LogInterceptor):
catalog.sql_uncatalog_object = tuple(sql_uncatalog_object)
# set filter for this method
expression = 'python: context.isDelivery()'
expr_instance = Expression(expression)
zsql_method.setFiltered(1)
zsql_method.setExpression(expression)
zsql_method.setExpressionInstance(expr_instance)
zsql_method.setExpressionCacheKey('portal_type')
zsql_method.setTypeList([])
......
......@@ -38,7 +38,6 @@ from Products.ZSQLCatalog.SQLCatalog import Catalog, CatalogError
import OFS.History
from AccessControl import ClassSecurityInfo
from Acquisition import aq_base
from Products.CMFCore.Expression import Expression
from zLOG import LOG, INFO, TRACE, WARNING, ERROR
import time
......@@ -393,12 +392,6 @@ class ERP5Catalog(Folder, Catalog):
return None
method.setExpression(expression)
if expression:
expression_instance = Expression(expression)
else:
expression_instance = None
method.setExpressionInstance(expression)
InitializeClass(ERP5Catalog)
class ERP5CatalogError(CatalogError): pass
......@@ -43,6 +43,7 @@ from Products.ERP5Type.tests.utils import createZODBPythonScript, todo_erp5, \
from Products.ZSQLCatalog.ZSQLCatalog import HOT_REINDEXING_FINISHED_STATE,\
HOT_REINDEXING_RECORDING_STATE, HOT_REINDEXING_DOUBLE_INDEXING_STATE
from Products.CMFActivity.Errors import ActivityFlushError
from Products.PageTemplates.Expressions import getEngine
from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery, SimpleQuery
......@@ -3964,6 +3965,34 @@ VALUES
finally:
del self.portal.portal_activities.__class__.doSomething
def test_filter_expression(self):
catalog = self.portal.portal_catalog.getSQLCatalog()
portal_type_list = catalog.getVisibleAllowedContentTypeList()
assert portal_type_list
econtext = getEngine().getContext()
getExpressionInstance = lambda: catalog._getFilterDict()[indexation_method_id].get('expression_instance')
evaluate = lambda: getExpressionInstance()(econtext)
catalog_method_list = catalog.getSqlCatalogObjectListList()
for portal_type in portal_type_list:
indexation_method = catalog.newContent(portal_type=portal_type)
indexation_method_id = indexation_method.getId()
catalog.setSqlCatalogObjectListList(catalog_method_list + (indexation_method_id, ))
try:
indexation_method.setFiltered(True)
indexation_method.setExpression('python: 1')
self.assertEqual(evaluate(), 1)
self.commit()
self.assertEqual(evaluate(), 1)
indexation_method.setExpression('python: 2')
self.abort()
self.assertEqual(evaluate(), 1)
indexation_method.setExpression('python: 2')
self.assertEqual(evaluate(), 2)
self.commit()
self.assertEqual(evaluate(), 2)
finally:
catalog.setSqlCatalogObjectListList(catalog_method_list)
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestERP5Catalog))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment