From c2eaab4b447ed1a0b020ef9e68aa3fe9d6817d09 Mon Sep 17 00:00:00 2001 From: Gaetan Delannay Date: Tue, 4 Sep 2012 18:00:22 +0200 Subject: [PATCH] [gen] Cleaner and more robust approach when using Zope database indexes. --- gen/__init__.py | 24 +++++++++++++++++------- gen/installer.py | 15 +++------------ gen/mixins/__init__.py | 6 +++++- gen/ui/widgets/ref.pt | 4 ++-- gen/wrappers/ToolWrapper.py | 35 ++++++++++++++++++++++++++++------- gen/wrappers/__init__.py | 21 ++++++++++++++++++++- 6 files changed, 75 insertions(+), 30 deletions(-) diff --git a/gen/__init__.py b/gen/__init__.py index 6079f9a..0d4f829 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -33,6 +33,13 @@ def initMasterValue(v): else: res = v return [str(v) for v in res] +# Default Appy indexes --------------------------------------------------------- +defaultIndexes = { + 'State': 'FieldIndex', 'UID': 'FieldIndex', 'Title': 'ZCTextIndex', + 'SortableTitle': 'FieldIndex', 'SearchableText': 'ZCTextIndex', + 'Creator': 'FieldIndex', 'Created': 'DateIndex', 'ClassName': 'FieldIndex', + 'Allowed': 'KeywordIndex'} + # Descriptor classes used for refining descriptions of elements in types # (pages, groups,...) ---------------------------------------------------------- class Page: @@ -961,14 +968,17 @@ class Type: try: return method(obj, self) except Exception, e: - # Log the initial error. - obj.log(tb, type='error') - if raiseOnError: raise te - else: return str(te) + if raiseOnError: + # Raise the initial error. + raise te + else: + obj.log(tb, type='error') + return str(te) except Exception, e: - obj.log(Traceback.get(), type='error') if raiseOnError: raise e - else: return str(e) + else: + obj.log(Traceback.get(), type='error') + return str(e) def process(self, obj): '''This method is a general hook allowing a field to perform some @@ -2122,7 +2132,7 @@ class Computed(Type): return self.callMacro(obj, self.method) else: # self.method is a method that will return the field value - return self.callMethod(obj, self.method, raiseOnError=False) + return self.callMethod(obj, self.method, raiseOnError=True) def getFormattedValue(self, obj, value): if not isinstance(value, basestring): return str(value) diff --git a/gen/installer.py b/gen/installer.py index 10dbe09..931f08a 100644 --- a/gen/installer.py +++ b/gen/installer.py @@ -163,10 +163,10 @@ class ZopeInstaller: catalog.addIndex(indexName, indexType) else: catalog.addIndex(indexName, indexType,extra=ZCTextIndexInfo) + # Indexing database content based on this index. catalog.reindexIndex(indexName, self.app.REQUEST) logger.info('Created index "%s" of type "%s"...' % \ (indexName, indexType)) - # Indexing database content based on this index. lexiconInfos = [ appy.Object(group='Case Normalizer', name='Case Normalizer'), @@ -188,19 +188,11 @@ class ZopeInstaller: elements=self.lexiconInfos) # Create or update Appy-wide indexes and field-related indexes - indexInfo = {'State': 'FieldIndex', 'UID': 'FieldIndex', - 'Title': 'ZCTextIndex', 'SortableTitle': 'FieldIndex', - 'SearchableText': 'ZCTextIndex', 'Creator': 'FieldIndex', - 'Created': 'DateIndex', 'ClassName': 'FieldIndex', - 'Allowed': 'KeywordIndex'} + indexInfo = gen.defaultIndexes.copy() tool = self.app.config for className in self.config.attributes.iterkeys(): wrapperClass = tool.getAppyClass(className, wrapper=True) - for appyType in wrapperClass.__fields__: - if not appyType.indexed or (appyType.name == 'title'): continue - n = appyType.name - indexName = 'get%s%s' % (n[0].upper(), n[1:]) - indexInfo[indexName] = appyType.getIndexType() + indexInfo.update(wrapperClass.getIndexes(includeDefaults=False)) self.installIndexes(indexInfo) def getAddPermission(self, className): @@ -345,7 +337,6 @@ class ZopeInstaller: appyTool.log('Translation "%s" updated from "%s".' % \ (translation.id, poName)) - def configureSessions(self): '''Configure the session machinery.''' # Register a function warning us when a session object is deleted. When diff --git a/gen/mixins/__init__.py b/gen/mixins/__init__.py index 6385cb5..a3824f8 100644 --- a/gen/mixins/__init__.py +++ b/gen/mixins/__init__.py @@ -321,7 +321,11 @@ class BaseMixin: if indexes: catalog.catalog_object(self, path, idxs=indexes) else: - catalog.catalog_object(self, path) + # Get the list of indexes that apply on this object. Else, Zope + # will reindex all indexes defined in the catalog, and through + # acquisition, wrong methods can be called on wrong objects. + iNames = self.wrapperClass.getIndexes().keys() + catalog.catalog_object(self, path, idxs=iNames) def say(self, msg, type='info'): '''Prints a p_msg in the user interface. p_logLevel may be "info", diff --git a/gen/ui/widgets/ref.pt b/gen/ui/widgets/ref.pt index 990a927..5ee9c42 100644 --- a/gen/ui/widgets/ref.pt +++ b/gen/ui/widgets/ref.pt @@ -143,7 +143,7 @@ If there is no object... - + @@ -171,7 +171,7 @@ No object is present -

+

diff --git a/gen/wrappers/ToolWrapper.py b/gen/wrappers/ToolWrapper.py index 4f1b4b7..c81a44d 100644 --- a/gen/wrappers/ToolWrapper.py +++ b/gen/wrappers/ToolWrapper.py @@ -168,20 +168,41 @@ class ToolWrapper(AbstractWrapper): # reindex all Appy-managed objects, ie those in folders "config" # and "data". # First, clear the catalog. + self.log('Recomputing the whole catalog...') app = self.o.getParentNode() app.catalog._catalog.clear() - app.config.reindex() nb = 1 + failed = [] for obj in app.config.objectValues(): - nb += self.refreshCatalog(startObject=obj) + subNb, subFailed = self.refreshCatalog(startObject=obj) + nb += subNb + failed += subFailed + try: + app.config.reindex() + except: + failed.append(app.config) # Then, refresh objects in the "data" folder. for obj in app.data.objectValues(): - nb += self.refreshCatalog(startObject=obj) - print '%d object(s) were reindexed.' % nb + subNb, subFailed = self.refreshCatalog(startObject=obj) + nb += subNb + failed += subFailed + # Re-try to index all objects for which reindexation has failed. + for obj in failed: obj.reindex() + if failed: + failMsg = ' (%d retried)' % len(failed) + else: + failMsg = '' + self.log('%d object(s) were reindexed%s.' % (nb, failMsg)) else: - startObject.reindex() nb = 1 + failed = [] for obj in startObject.objectValues(): - nb += self.refreshCatalog(startObject=obj) - return nb + subNb, subFailed = self.refreshCatalog(startObject=obj) + nb += subNb + failed += subFailed + try: + startObject.reindex() + except Exception, e: + failed.append(startObject) + return nb, failed # ------------------------------------------------------------------------------ diff --git a/gen/wrappers/__init__.py b/gen/wrappers/__init__.py index c1378fc..5697574 100644 --- a/gen/wrappers/__init__.py +++ b/gen/wrappers/__init__.py @@ -4,7 +4,8 @@ # ------------------------------------------------------------------------------ import os, os.path, mimetypes import appy.pod -from appy.gen import Type, Search, Ref, String, WorkflowAnonymous +from appy.gen import Type, Search, Ref, String, WorkflowAnonymous, \ + defaultIndexes from appy.gen.utils import createObject from appy.shared.utils import getOsTempFolder, executeCommand, \ normalizeString, sequenceTypes @@ -59,6 +60,24 @@ class AbstractWrapper(object): if not res: res = cfg.defaultAddRoles return res + @classmethod + def getIndexes(klass, includeDefaults=True): + '''Returns a dict whose keys are the names of the indexes that are + applicable to instances of this class, and whose values are the + (Zope) types of those indexes.''' + # Start with the standard indexes applicable for any Appy class. + if includeDefaults: + res = defaultIndexes.copy() + else: + res = {} + # Add the indexed fields found on this class + for field in klass.__fields__: + if not field.indexed or (field.name == 'title'): continue + n = field.name + indexName = 'get%s%s' % (n[0].upper(), n[1:]) + res[indexName] = field.getIndexType() + return res + # -------------------------------------------------------------------------- # Instance methods # --------------------------------------------------------------------------