diff --git a/product/ZSQLCatalog/SQLCatalog.py b/product/ZSQLCatalog/SQLCatalog.py index e24ad802b0d8f889f927578ca1a531966c593d22..6a26069c7c73f40692a6071a6478d2a239f649d9 100644 --- a/product/ZSQLCatalog/SQLCatalog.py +++ b/product/ZSQLCatalog/SQLCatalog.py @@ -40,6 +40,7 @@ from ZODB.POSException import ConflictError from Products.CMFCore import permissions from Products.PythonScripts.Utility import allow_class +from compiler.consts import CO_VARKEYWORDS from functools import wraps import time import urllib @@ -2564,14 +2565,47 @@ class SearchKeyWrapperForScriptableKey(SearchKey.SearchKey.SearchKey): This SearchKey is a simple wrapper around a ScriptableKey, so such script can be used in place of a regular SearchKey. """ + security = ClassSecurityInfo() def __init__(self, column, script): self.script = script + func_code = script.func_code + self.buildQuery = self.__buildQueryWithFiveArgumentsAPI if ( + # 5: search_value (under any name), "search_key", "group", + # "logical_operator", "comparison_operator". The last 4 are possible in + # any order. + func_code.co_argcount == 5 or + getattr(func_code, 'co_flags', 0) & CO_VARKEYWORDS + ) else self.__buildQueryWithOldAPI super(SearchKeyWrapperForScriptableKey, self).__init__(column) - def buildQuery(self, search_value, group=None, logical_operator=None, - comparison_operator=None): - # XXX: It would be better to extend ScriptableKey API to support other - # parameters. + security.declarePublic('processSearchValue') + def processSearchValue(self, *args, **kw): + """ + Expose _processSearchValue to self.script. + Only callable from full-API scripts, as others do not get access to our + instance. + """ + return self._processSearchValue(*args, **kw) + + def __buildQueryWithFiveArgumentsAPI(self, search_value, group=None, + logical_operator=None, comparison_operator=None): + """ + Becomes buildQuery if self.script supports extra parameters. + """ + assert logical_operator in (None, 'and', 'or'), repr(logical_operator) + return self.script( + search_value, + search_key=self, + group=group, + logical_operator=logical_operator, + comparison_operator=comparison_operator, + ) + + def __buildQueryWithOldAPI(self, search_value, group=None, + logical_operator=None, comparison_operator=None): + """ + Becomes buildQuery if self.script does not support extra parameters. + """ if group is not None: raise ValueError( 'ScriptableKey cannot be used inside a group (%r given).' % (group, ), @@ -2585,6 +2619,7 @@ class SearchKeyWrapperForScriptableKey(SearchKey.SearchKey.SearchKey): 'ScriptableKey ignores comparison operators (%r given).' % (comparison_operator, ), ) return self.script(search_value) +InitializeClass(SearchKeyWrapperForScriptableKey) from Operator import operator_dict def getComparisonOperatorInstance(operator): diff --git a/product/ZSQLCatalog/tests/testSQLCatalog.py b/product/ZSQLCatalog/tests/testSQLCatalog.py index 323e064144fb8c4b6fb02ced968597e0745ed4a9..86f6341aaa595818fbdacd808d8aae2d234cd6c4 100644 --- a/product/ZSQLCatalog/tests/testSQLCatalog.py +++ b/product/ZSQLCatalog/tests/testSQLCatalog.py @@ -170,7 +170,10 @@ class DummyCatalog(SQLCatalog): sql_catalog_keyword_search_keys = ('keyword', ) sql_catalog_datetime_search_keys = ('date', ) sql_catalog_full_text_search_keys = ('old_fulltext', ) - sql_catalog_scriptable_keys = ('scriptable_keyword | scriptableKeyScript', ) + sql_catalog_scriptable_keys = ( + 'scriptable_keyword | scriptableKeyScript', + 'scriptable_keyword_5args | scriptableKeyScriptFiveArguments', + ) sql_catalog_search_keys = ('fulltext | MroongaFullTextKey', 'fulltext_boolean | MroongaBooleanFullTextKey',) @@ -218,6 +221,39 @@ class DummyCatalog(SQLCatalog): """ return SimpleQuery(comparison_operator='=', keyword=value) + @staticmethod + def scriptableKeyScriptFiveArguments( + value, + search_key, + group, + logical_operator, + comparison_operator, + ): + """ + Mimics a scriptable key (PythonScript) subobject, using the SearchKey API. + """ + operator_value_dict, logical_operator, _ = search_key.processSearchValue( + search_value=value, + default_logical_operator=logical_operator, + comparison_operator=comparison_operator, + ) + query_list = [ + SimpleQuery( + keyword=value_list[0], # XXX: Fine for tests, bad in general. + comparison_operator=comparison_operator, + group=group, + ) + for comparison_operator, value_list in operator_value_dict.iteritems() + ] + if len(query_list) == 1: + return query_list[0] + if query_list: + return ComplexQuery( + query_list, + logical_operator=logical_operator, + ) + return SimpleQuery(uid=-1) + class TestSQLCatalog(ERP5TypeTestCase): def setUp(self): self._catalog = DummyCatalog('dummy_catalog') @@ -535,6 +571,8 @@ class TestSQLCatalog(ERP5TypeTestCase): {'scriptable_keyword': '%a%'}) self.catalog(ReferenceQuery(ReferenceQuery(operator='=', keyword='%a%'), operator='and'), {'default': 'scriptable_keyword:%a%'}) + self.catalog(ReferenceQuery(ReferenceQuery(operator='!=', keyword='a'), operator='and'), + {'scriptable_keyword_5args': '!=a'}) def test_008_testRawKey(self): self.catalog(ReferenceQuery(ReferenceQuery(operator='=', default='%a%'), operator='and'),