From 33170cecea172a650611855d38c5c6cb0789a7b3 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Fri, 1 Dec 2017 20:56:22 +0100 Subject: [PATCH 01/29] Make the DX factory work in a GS baseline import work. ps resolveDottedName either returns or raises --- CHANGES.rst | 4 +++- plone/dexterity/factory.py | 2 +- plone/dexterity/fti.py | 8 +++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d20d9c87..fa17f0d4 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -15,7 +15,9 @@ New features: Bug fixes: -- *add item here* +- Fix ft._updateProperty so it doesn't break when receiving an empty value. + This happens when an DX FTI is part of a Generic Setup baseline import. + [jaroel] 2.6.0 (2018-04-03) diff --git a/plone/dexterity/factory.py b/plone/dexterity/factory.py index 9ee459c8..9949213e 100644 --- a/plone/dexterity/factory.py +++ b/plone/dexterity/factory.py @@ -30,7 +30,7 @@ def description(self): def __call__(self, *args, **kw): fti = getUtility(IDexterityFTI, name=self.portal_type) - klass = resolveDottedName(fti.klass) + klass = resolveDottedName(fti.klass) if fti.klass else None if klass is None or not callable(klass): raise ValueError( "Content class %s set for type %s is not valid" % diff --git a/plone/dexterity/fti.py b/plone/dexterity/fti.py index 265a84df..b544f216 100644 --- a/plone/dexterity/fti.py +++ b/plone/dexterity/fti.py @@ -186,7 +186,7 @@ def __init__(self, *args, **kwargs): # Set the content_meta_type from the klass - klass = utils.resolveDottedName(self.klass) + klass = utils.resolveDottedName(self.klass) if self.klass else None if klass is not None: self.content_meta_type = getattr(klass, 'meta_type', None) @@ -218,7 +218,7 @@ def Metatype(self): if self.content_meta_type: return self.content_meta_type # BBB - this didn't use to be set - klass = utils.resolveDottedName(self.klass) + klass = utils.resolveDottedName(self.klass) if self.klass else None if klass is not None: self.content_meta_type = getattr(klass, 'meta_type', None) return self.content_meta_type @@ -291,7 +291,9 @@ def _updateProperty(self, id, value): # Update meta_type from klass if id == 'klass': - klass = utils.resolveDottedName(new_value) + klass = None + if new_value: + klass = utils.resolveDottedName(new_value) if klass is not None: self.content_meta_type = getattr(klass, 'meta_type', None) From 9e036ea59eaf205beb16564b0efcd1725fd6e846 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 23 Jun 2018 23:42:45 +0200 Subject: [PATCH 02/29] Pass on attribute access to item access + log a bunch --- plone/dexterity/content.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index efcf0147..e19b1726 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -48,6 +48,7 @@ from zope.security.interfaces import IPermission import six +import warnings _marker = object() @@ -704,6 +705,10 @@ def __init__(self, id=None, **kwargs): DexterityContent.__init__(self, id, **kwargs) def __getattr__(self, name): + if name in self: + msg = "Trying to access item '{}' in {} by attribute".format(name, self) + warnings.warn(msg) + try: return DexterityContent.__getattr__(self, name) except AttributeError: @@ -712,6 +717,36 @@ def __getattr__(self, name): # Be specific about the implementation we use return CMFOrderedBTreeFolderBase.__getattr__(self, name) + def __setattr__(self, name, value): + try: + has_item_by_name = name in self + except: + has_item_by_name = False + if has_item_by_name: + msg = ( + "An item with the same name already exists. Use item access, for example: obj['{}'] = value. " + "We are setting the item instead of the attribute!" + ).format(name) + warnings.warn(msg) + # We remove the old one and then add the new one so the old one is + # unindexed and the new is indexed. + del self[name] + self[name] = value + else: + super(Container, self).__setattr__(name, value) + + def _delObject(self, name, *args, **kwargs): + super(Container, self)._delObject(name, *args, **kwargs) + + # This will trigger when an item was deleted from this container and + # attribute by the same name exists. ie obj['my_id'] and obj.my_id. + if getattr(aq_base(self), name, _marker) is not _marker: + msg = ( + "Item '{}' contained in {} was shadowed by an attribute." + "You might want to delete the attribute as well." + ).format(name, self) + warnings.warn(msg) + @security.protected(permissions.DeleteObjects) def manage_delObjects(self, ids=None, REQUEST=None): """Delete the contained objects with the specified ids. From 71796f65a27c1d9b130441df71cadc6b8ec95151 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 16 Feb 2019 16:32:01 +0100 Subject: [PATCH 03/29] Check __dict__ as getattr will use descriptors - ie FieldProperties --- plone/dexterity/content.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index d457327d..93389f0f 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -752,7 +752,7 @@ def _delObject(self, name, *args, **kwargs): # This will trigger when an item was deleted from this container and # attribute by the same name exists. ie obj['my_id'] and obj.my_id. - if getattr(aq_base(self), name, _marker) is not _marker: + if name in aq_base(self).__dict__: msg = ( "Item '{}' contained in {} was shadowed by an attribute." "You might want to delete the attribute as well." From 20fdb8aa54d8a12e650c22a4944d33c5c08f0bc9 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 3 Mar 2019 12:21:13 +0100 Subject: [PATCH 04/29] Donn't warning about accessing items by attribute. --- plone/dexterity/content.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 93389f0f..6645d48f 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -717,10 +717,6 @@ def __init__(self, id=None, **kwargs): DexterityContent.__init__(self, id, **kwargs) def __getattr__(self, name): - if name in self: - msg = "Trying to access item '{}' in {} by attribute".format(name, self) - warnings.warn(msg) - try: return DexterityContent.__getattr__(self, name) except AttributeError: From 44523c93f5e9cd327115869a79089e8cd3d39d04 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 3 Mar 2019 13:50:56 +0100 Subject: [PATCH 05/29] Pass in knnown prefix --- plone/dexterity/fti.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plone/dexterity/fti.py b/plone/dexterity/fti.py index 7794936d..7b144497 100644 --- a/plone/dexterity/fti.py +++ b/plone/dexterity/fti.py @@ -264,8 +264,11 @@ def lookupSchema(self): # Otherwise, look up a dynamic schema. This will query the model for # an unnamed schema if it is the first time it is looked up. # See schema.py - - schemaName = portalTypeToSchemaName(self.getId()) + try: + prefix = self.__parent__.__name__ + except AttributeError: + prefix = None + schemaName = portalTypeToSchemaName(self.getId(), prefix=prefix) return getattr(plone.dexterity.schema.generated, schemaName) def lookupModel(self): From d341c7138bc4fde066c33b5ab931e0367fb9b14f Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 3 Mar 2019 22:40:02 +0100 Subject: [PATCH 06/29] fix up, look sharp --- plone/dexterity/content.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 6645d48f..9f352142 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -725,21 +725,23 @@ def __getattr__(self, name): # Be specific about the implementation we use return CMFOrderedBTreeFolderBase.__getattr__(self, name) - def __setattr__(self, name, value): + def __delattr__(self, name): try: - has_item_by_name = name in self - except: - has_item_by_name = False - if has_item_by_name: - msg = ( - "An item with the same name already exists. Use item access, for example: obj['{}'] = value. " - "We are setting the item instead of the attribute!" - ).format(name) - warnings.warn(msg) - # We remove the old one and then add the new one so the old one is - # unindexed and the new is indexed. + super(Container, self).__delattr__(name) + except AttributeError: # delete the item instead del self[name] + + def __setattr__(self, name, value): + # If we have an existing attribute, just set it. + # We'll check this first, so we don't check the tree unneeded. + if name in self.__dict__: + super(Container, self).__setattr__(name, value) + + # if we have a n item, set that + elif '_tree' in self.__dict__ and name in self: self[name] = value + + # else we'll set an attribute. else: super(Container, self).__setattr__(name, value) From 4877506ccb65d8a3875381f6f8d0a833432bf5c2 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 3 Mar 2019 23:09:10 +0100 Subject: [PATCH 07/29] fu. We do need to delete the item first, or we'll get doublicate ids. --- plone/dexterity/content.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 9f352142..94ad5797 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -739,6 +739,7 @@ def __setattr__(self, name, value): # if we have a n item, set that elif '_tree' in self.__dict__ and name in self: + del self[name] self[name] = value # else we'll set an attribute. From bde13fcb802f9dff19186bd1f18b5f934952b4f6 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 21 Apr 2019 23:18:33 +0200 Subject: [PATCH 08/29] prevent some loop where getUtility(ISiteRoot) tiggers gettings the schema, which triggers portalTypeToSchemaName, etc. --- plone/dexterity/fti.py | 9 +++++---- plone/dexterity/schema.py | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/plone/dexterity/fti.py b/plone/dexterity/fti.py index 7b144497..bcdca294 100644 --- a/plone/dexterity/fti.py +++ b/plone/dexterity/fti.py @@ -265,10 +265,10 @@ def lookupSchema(self): # an unnamed schema if it is the first time it is looked up. # See schema.py try: - prefix = self.__parent__.__name__ + siteroot = self.__parent__.__parent__ except AttributeError: - prefix = None - schemaName = portalTypeToSchemaName(self.getId(), prefix=prefix) + siteroot = None + schemaName = portalTypeToSchemaName(self.getId(), siteroot=siteroot) return getattr(plone.dexterity.schema.generated, schemaName) def lookupModel(self): @@ -558,7 +558,8 @@ def ftiModified(object, event): if (fti.model_source or fti.model_file) \ and ('model_source' in mod or 'model_file' in mod or 'schema_policy' in mod): - schemaName = portalTypeToSchemaName(portal_type) + siteroot = fti.__parent__.__parent__ + schemaName = portalTypeToSchemaName(portal_type, siteroot=siteroot) schema = getattr(plone.dexterity.schema.generated, schemaName) model = fti.lookupModel() diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 93565ea4..38e8d41a 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -281,11 +281,13 @@ def split(self, s): return [self.decode(a) for a in s.split('_0_')] -def portalTypeToSchemaName(portal_type, schema=u"", prefix=None): +def portalTypeToSchemaName(portal_type, schema=u"", prefix=None, siteroot=None): """Return a canonical interface name for a generated schema interface. """ if prefix is None: - prefix = '/'.join(getUtility(ISiteRoot).getPhysicalPath())[1:] + if siteroot is None: + siteroot = getUtility(ISiteRoot) + prefix = '/'.join(siteroot.getPhysicalPath())[1:] encoder = SchemaNameEncoder() return encoder.join(prefix, portal_type, schema) From 48b0bc5aa2c40c0c4fbae2402ccccdd33e833fa8 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 21 Apr 2019 23:48:39 +0200 Subject: [PATCH 09/29] blergh --- plone/dexterity/fti.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/plone/dexterity/fti.py b/plone/dexterity/fti.py index bcdca294..e47ebf6d 100644 --- a/plone/dexterity/fti.py +++ b/plone/dexterity/fti.py @@ -264,10 +264,11 @@ def lookupSchema(self): # Otherwise, look up a dynamic schema. This will query the model for # an unnamed schema if it is the first time it is looked up. # See schema.py - try: - siteroot = self.__parent__.__parent__ - except AttributeError: - siteroot = None + siteroot = self + while True: + if siteroot is None or ISiteRoot.providedBy(siteroot): + break + siteroot = getattr(siteroot, '__parent__', None) schemaName = portalTypeToSchemaName(self.getId(), siteroot=siteroot) return getattr(plone.dexterity.schema.generated, schemaName) @@ -558,7 +559,11 @@ def ftiModified(object, event): if (fti.model_source or fti.model_file) \ and ('model_source' in mod or 'model_file' in mod or 'schema_policy' in mod): - siteroot = fti.__parent__.__parent__ + siteroot = fti + while True: + if siteroot is None or ISiteRoot.providedBy(siteroot): + break + siteroot = getattr(siteroot, '__parent__', None) schemaName = portalTypeToSchemaName(portal_type, siteroot=siteroot) schema = getattr(plone.dexterity.schema.generated, schemaName) From 32b8912ce1a33cb0ec19e16efdcac131550c31f3 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 28 Apr 2019 23:54:11 +0200 Subject: [PATCH 10/29] kekekekekek --- plone/dexterity/fti.py | 14 ++------------ plone/dexterity/schema.py | 6 +++--- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/plone/dexterity/fti.py b/plone/dexterity/fti.py index e47ebf6d..888f34ea 100644 --- a/plone/dexterity/fti.py +++ b/plone/dexterity/fti.py @@ -264,12 +264,7 @@ def lookupSchema(self): # Otherwise, look up a dynamic schema. This will query the model for # an unnamed schema if it is the first time it is looked up. # See schema.py - siteroot = self - while True: - if siteroot is None or ISiteRoot.providedBy(siteroot): - break - siteroot = getattr(siteroot, '__parent__', None) - schemaName = portalTypeToSchemaName(self.getId(), siteroot=siteroot) + schemaName = portalTypeToSchemaName(self.getId()) return getattr(plone.dexterity.schema.generated, schemaName) def lookupModel(self): @@ -559,12 +554,7 @@ def ftiModified(object, event): if (fti.model_source or fti.model_file) \ and ('model_source' in mod or 'model_file' in mod or 'schema_policy' in mod): - siteroot = fti - while True: - if siteroot is None or ISiteRoot.providedBy(siteroot): - break - siteroot = getattr(siteroot, '__parent__', None) - schemaName = portalTypeToSchemaName(portal_type, siteroot=siteroot) + schemaName = portalTypeToSchemaName(portal_type) schema = getattr(plone.dexterity.schema.generated, schemaName) model = fti.lookupModel() diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 38e8d41a..e0e271f9 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -16,6 +16,7 @@ from zope.component import getAllUtilitiesRegisteredFor from zope.component import getUtility from zope.component import queryUtility +from zope.component.hooks import getSite from zope.dottedname.resolve import resolve from zope.interface import alsoProvides from zope.interface import implementer @@ -281,12 +282,11 @@ def split(self, s): return [self.decode(a) for a in s.split('_0_')] -def portalTypeToSchemaName(portal_type, schema=u"", prefix=None, siteroot=None): +def portalTypeToSchemaName(portal_type, schema=u"", prefix=None): """Return a canonical interface name for a generated schema interface. """ if prefix is None: - if siteroot is None: - siteroot = getUtility(ISiteRoot) + siteroot = getSite() prefix = '/'.join(siteroot.getPhysicalPath())[1:] encoder = SchemaNameEncoder() From 123cb87a771dd2e24d58b00e9a21a5e87295abc6 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 22 Jun 2019 10:15:20 +0200 Subject: [PATCH 11/29] just use getUtility --- plone/dexterity/schema.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index e0e271f9..6a4afd61 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -286,7 +286,7 @@ def portalTypeToSchemaName(portal_type, schema=u"", prefix=None): """Return a canonical interface name for a generated schema interface. """ if prefix is None: - siteroot = getSite() + siteroot = getUtility(ISiteRoot) prefix = '/'.join(siteroot.getPhysicalPath())[1:] encoder = SchemaNameEncoder() From b9233a9024ee08c0a57df8be0756b1a353141342 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 22 Jun 2019 15:25:42 +0200 Subject: [PATCH 12/29] special case Plone Site to we don't loop --- plone/dexterity/schema.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 6a4afd61..39a8e896 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -286,7 +286,11 @@ def portalTypeToSchemaName(portal_type, schema=u"", prefix=None): """Return a canonical interface name for a generated schema interface. """ if prefix is None: - siteroot = getUtility(ISiteRoot) + if portal_type == 'Plone Site': + fti = getUtility(IDexterityFTI, name=portal_type) + siteroot = fti.__parent__ + else: + siteroot = getUtility(ISiteRoot) prefix = '/'.join(siteroot.getPhysicalPath())[1:] encoder = SchemaNameEncoder() From e6bd7fe602efb6b3ef1f3c49ce7e22ac2f08f2b6 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Tue, 10 Dec 2019 17:31:46 +0100 Subject: [PATCH 13/29] maybe you're gonna be that one that --- plone/dexterity/content.py | 60 ++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 29 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index f9286d88..a5dc04c1 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -766,35 +766,37 @@ def __init__(self, id=None, **kwargs): CMFOrderedBTreeFolderBase.__init__(self, id) DexterityContent.__init__(self, id, **kwargs) - def __getattr__(self, name): - try: - return DexterityContent.__getattr__(self, name) - except AttributeError: - pass - - # Be specific about the implementation we use - return CMFOrderedBTreeFolderBase.__getattr__(self, name) - - def __delattr__(self, name): - try: - super(Container, self).__delattr__(name) - except AttributeError: # delete the item instead - del self[name] - - def __setattr__(self, name, value): - # If we have an existing attribute, just set it. - # We'll check this first, so we don't check the tree unneeded. - if name in self.__dict__: - super(Container, self).__setattr__(name, value) - - # if we have a n item, set that - elif '_tree' in self.__dict__ and name in self: - del self[name] - self[name] = value - - # else we'll set an attribute. - else: - super(Container, self).__setattr__(name, value) + # def __getattr__(self, name): + # # if name[0] != '_': + # # print('Container.__getattr__', self, name) + # try: + # return DexterityContent.__getattr__(self, name) + # except AttributeError: + # pass + + # # Be specific about the implementation we use + # return CMFOrderedBTreeFolderBase.__getattr__(self, name) + + # def __delattr__(self, name): + # try: + # super(Container, self).__delattr__(name) + # except AttributeError: # delete the item instead + # del self[name] + + # def __setattr__(self, name, value): + # # If we have an existing attribute, just set it. + # # We'll check this first, so we don't check the tree unneeded. + # if name in self.__dict__: + # super(Container, self).__setattr__(name, value) + + # # if we have a n item, set that + # elif '_tree' in self.__dict__ and name in self: + # del self[name] + # self[name] = value + + # # else we'll set an attribute. + # else: + # super(Container, self).__setattr__(name, value) def _delObject(self, name, *args, **kwargs): super(Container, self)._delObject(name, *args, **kwargs) From 9d40507e3c6d3568ed4a33cb73eec64e64a29c8f Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Wed, 11 Dec 2019 12:47:02 +0100 Subject: [PATCH 14/29] i won't know --- plone/dexterity/content.py | 40 +++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index a5dc04c1..bd17529f 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -766,22 +766,20 @@ def __init__(self, id=None, **kwargs): CMFOrderedBTreeFolderBase.__init__(self, id) DexterityContent.__init__(self, id, **kwargs) - # def __getattr__(self, name): - # # if name[0] != '_': - # # print('Container.__getattr__', self, name) - # try: - # return DexterityContent.__getattr__(self, name) - # except AttributeError: - # pass - - # # Be specific about the implementation we use - # return CMFOrderedBTreeFolderBase.__getattr__(self, name) - - # def __delattr__(self, name): - # try: - # super(Container, self).__delattr__(name) - # except AttributeError: # delete the item instead - # del self[name] + def __getattr__(self, name): + try: + return DexterityContent.__getattr__(self, name) + except AttributeError: + pass + + # Be specific about the implementation we use + return CMFOrderedBTreeFolderBase.__getattr__(self, name) + + def __delattr__(self, name): + try: + super(Container, self).__delattr__(name) + except AttributeError: # delete the item instead + del self[name] # def __setattr__(self, name, value): # # If we have an existing attribute, just set it. @@ -798,6 +796,16 @@ def __init__(self, id=None, **kwargs): # else: # super(Container, self).__setattr__(name, value) + # def __setattr__(self, name, value): + # # if we have an item, set that + # if '_tree' in self.__dict__ and name in self: + # # If an object by the given id already exists, remove it. + # self._delObject(name) + # self._setObject(name, value) + # # else we'll set an attribute. + # else: + # super(Container, self).__setattr__(name, value) + def _delObject(self, name, *args, **kwargs): super(Container, self)._delObject(name, *args, **kwargs) From c23174c2788883dcd71f1ad685074cc61541b11e Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Mon, 30 Dec 2019 22:53:05 +0100 Subject: [PATCH 15/29] don't think we need this --- plone/dexterity/content.py | 42 -------------------------------------- 1 file changed, 42 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 84b77b3e..1c271121 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -776,48 +776,6 @@ def __getattr__(self, name): # Be specific about the implementation we use return CMFOrderedBTreeFolderBase.__getattr__(self, name) - def __delattr__(self, name): - try: - super(Container, self).__delattr__(name) - except AttributeError: # delete the item instead - del self[name] - - # def __setattr__(self, name, value): - # # If we have an existing attribute, just set it. - # # We'll check this first, so we don't check the tree unneeded. - # if name in self.__dict__: - # super(Container, self).__setattr__(name, value) - - # # if we have a n item, set that - # elif '_tree' in self.__dict__ and name in self: - # del self[name] - # self[name] = value - - # # else we'll set an attribute. - # else: - # super(Container, self).__setattr__(name, value) - - # def __setattr__(self, name, value): - # # if we have an item, set that - # if '_tree' in self.__dict__ and name in self: - # # If an object by the given id already exists, remove it. - # self._delObject(name) - # self._setObject(name, value) - # # else we'll set an attribute. - # else: - # super(Container, self).__setattr__(name, value) - - def _delObject(self, name, *args, **kwargs): - super(Container, self)._delObject(name, *args, **kwargs) - - # This will trigger when an item was deleted from this container and - # attribute by the same name exists. ie obj['my_id'] and obj.my_id. - if name in aq_base(self).__dict__: - msg = ( - "Item '{}' contained in {} was shadowed by an attribute." - "You might want to delete the attribute as well." - ).format(name, self) - warnings.warn(msg) @security.protected(permissions.DeleteObjects) def manage_delObjects(self, ids=None, REQUEST=None): From ef2e534aee67f46b41f2a93f27fbfd82913aa5d2 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Mon, 30 Dec 2019 22:56:43 +0100 Subject: [PATCH 16/29] Special case for Plone Site in lookup_fti --- plone/dexterity/schema.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 41a246b5..4fad1d5a 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -66,9 +66,19 @@ def lookup_fti(portal_type, cache=True): if fti_cache is None: fti_cache = dict() setattr(request, FTI_CACHE_KEY, fti_cache) - if portal_type in fti_cache: - fti = fti_cache[portal_type] - else: + fti = fti_cache.get(portal_type) + if fti is not None: + # The first time we get the Plone Site FTI it doesn't know + # everything by the looks of it. Maybe because of behavior registration + # or something like that. *hrugs* + if portal_type == 'Plone Site': + if not getattr(request, '_dx_FTI_CACHE_seen_plonesite_fti', False): + del fti_cache[portal_type] + fti = None + else: + request._dx_FTI_CACHE_seen_plonesite_fti = True + + if fti is None: fti_cache[portal_type] = fti = queryUtility( IDexterityFTI, name=portal_type From bded94333a38db3aac69b082fa33fda1bebdcd1a Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 30 May 2020 18:28:41 +0200 Subject: [PATCH 17/29] We no longer seem to be needing this --- plone/dexterity/schema.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 4fad1d5a..657dd20e 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -65,20 +65,11 @@ def lookup_fti(portal_type, cache=True): fti_cache = getattr(request, FTI_CACHE_KEY, None) if fti_cache is None: fti_cache = dict() + fti_cache['Plone Site'] = None setattr(request, FTI_CACHE_KEY, fti_cache) - fti = fti_cache.get(portal_type) - if fti is not None: - # The first time we get the Plone Site FTI it doesn't know - # everything by the looks of it. Maybe because of behavior registration - # or something like that. *hrugs* - if portal_type == 'Plone Site': - if not getattr(request, '_dx_FTI_CACHE_seen_plonesite_fti', False): - del fti_cache[portal_type] - fti = None - else: - request._dx_FTI_CACHE_seen_plonesite_fti = True - - if fti is None: + if portal_type in fti_cache: + fti = fti_cache[portal_type] + else: fti_cache[portal_type] = fti = queryUtility( IDexterityFTI, name=portal_type From 4c505b3652101f9c979c91d962716482ce45a55b Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 14 Jun 2020 13:27:58 +0200 Subject: [PATCH 18/29] i'm an idiot. 'mmm why would Plone Site have no fields?' --- plone/dexterity/schema.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 657dd20e..53593bcb 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -65,11 +65,12 @@ def lookup_fti(portal_type, cache=True): fti_cache = getattr(request, FTI_CACHE_KEY, None) if fti_cache is None: fti_cache = dict() - fti_cache['Plone Site'] = None setattr(request, FTI_CACHE_KEY, fti_cache) + fti = None if portal_type in fti_cache: fti = fti_cache[portal_type] - else: + + if fti is None: fti_cache[portal_type] = fti = queryUtility( IDexterityFTI, name=portal_type From 8ca5a1db73aa191390fd55d1f792e3cace2e3f44 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 19 Jul 2020 12:56:32 +0200 Subject: [PATCH 19/29] Ignore 'utilities' in DX getattr --- plone/dexterity/content.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 1c271121..63eb07f0 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -72,6 +72,7 @@ 'portal_placeful_workflow', 'portal_properties', 'translation_service', + 'utilities', ) ASSIGNABLE_CACHE_KEY = '__plone_dexterity_assignable_cache__' From 49d33b22ccbd5fae3f9aa76e9144bd0453a4910d Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sun, 19 Jul 2020 14:11:31 +0200 Subject: [PATCH 20/29] Revert "Ignore 'utilities' in DX getattr" An utilities attibute could be anything for non-site root objects This reverts commit 8ca5a1db73aa191390fd55d1f792e3cace2e3f44. --- plone/dexterity/content.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 63eb07f0..1c271121 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -72,7 +72,6 @@ 'portal_placeful_workflow', 'portal_properties', 'translation_service', - 'utilities', ) ASSIGNABLE_CACHE_KEY = '__plone_dexterity_assignable_cache__' From 617111d28237322c363f46a58ad13b375133f36f Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 22 Aug 2020 21:52:36 +0200 Subject: [PATCH 21/29] Don't allow shadowing items in the btree by attributes --- plone/dexterity/content.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 1c271121..4fb45c1b 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -776,6 +776,10 @@ def __getattr__(self, name): # Be specific about the implementation we use return CMFOrderedBTreeFolderBase.__getattr__(self, name) + def __setattr__(self, name, obj): + if self._tree is not None and name in self: + raise ValueError("Trying to set an item via attribute.") + super().__setattr__(name, obj) @security.protected(permissions.DeleteObjects) def manage_delObjects(self, ids=None, REQUEST=None): From 66753b30bcb37e4e1b0ee58f0c2fef57df02dc5f Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 22 Aug 2020 22:11:12 +0200 Subject: [PATCH 22/29] herf --- plone/dexterity/content.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 4fb45c1b..d8dc6c95 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -774,11 +774,15 @@ def __getattr__(self, name): pass # Be specific about the implementation we use - return CMFOrderedBTreeFolderBase.__getattr__(self, name) + if self._tree is not None: + return CMFOrderedBTreeFolderBase.__getattr__(self, name) + + raise AttributeError(name) def __setattr__(self, name, obj): if self._tree is not None and name in self: - raise ValueError("Trying to set an item via attribute.") + del self[name] + self[name] = obj super().__setattr__(name, obj) @security.protected(permissions.DeleteObjects) From ed3db7ee4bdb21f1b07d1cfa16be9e91117ec66a Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 26 Sep 2020 18:28:39 +0200 Subject: [PATCH 23/29] make py2 tests happy --- plone/dexterity/content.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index d8dc6c95..4fe6567d 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -781,9 +781,10 @@ def __getattr__(self, name): def __setattr__(self, name, obj): if self._tree is not None and name in self: + # We're trying to set an item via dotted name... del self[name] self[name] = obj - super().__setattr__(name, obj) + super(Container, self).__setattr__(name, obj) @security.protected(permissions.DeleteObjects) def manage_delObjects(self, ids=None, REQUEST=None): From 4ca53ebc29552051430017a9f5cecb611e8be29a Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Wed, 9 Dec 2020 21:45:48 +0100 Subject: [PATCH 24/29] Try to come up with a prefix only when there is none --- plone/dexterity/schema.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 82609828..ededc346 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -328,12 +328,13 @@ def split(self, s): def portalTypeToSchemaName(portal_type, schema=u"", prefix=None, suffix=None): """Return a canonical interface name for a generated schema interface. """ - if portal_type == 'Plone Site': - fti = getUtility(IDexterityFTI, name=portal_type) - siteroot = fti.__parent__ - else: - siteroot = getUtility(ISiteRoot) - prefix = '/'.join(siteroot.getPhysicalPath())[1:] + if prefix is None: + if portal_type == 'Plone Site': + fti = getUtility(IDexterityFTI, name=portal_type) + siteroot = fti.__parent__ + else: + siteroot = getUtility(ISiteRoot) + prefix = '/'.join(siteroot.getPhysicalPath())[1:] if suffix: prefix = '|'.join([prefix, suffix]) From 472d60c30bb9a5c527d3b9fae8a1c3a9f6d3b5c5 Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Sat, 12 Dec 2020 15:38:45 +0100 Subject: [PATCH 25/29] Don't try to be smart? --- plone/dexterity/content.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index 4fe6567d..dcb619f3 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -779,12 +779,14 @@ def __getattr__(self, name): raise AttributeError(name) - def __setattr__(self, name, obj): - if self._tree is not None and name in self: - # We're trying to set an item via dotted name... - del self[name] - self[name] = obj - super(Container, self).__setattr__(name, obj) + # XXX do we really want to handle this kind of cases, or is this a ID-10-T + # thingy? + # def __setattr__(self, name, obj): + # if self._tree is not None and name in self: + # # We're trying to set an item via dotted name... + # del self[name] + # self[name] = obj + # super(Container, self).__setattr__(name, obj) @security.protected(permissions.DeleteObjects) def manage_delObjects(self, ids=None, REQUEST=None): From a375f3c7eb36e46dc278de182fbd411e149181ef Mon Sep 17 00:00:00 2001 From: Roel Bruggink <roel@jaroel.nl> Date: Thu, 17 Dec 2020 14:52:18 +0100 Subject: [PATCH 26/29] Move changelog entry to news/85.bugfix --- CHANGES.rst | 5 +---- news/85.bugfix | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 news/85.bugfix diff --git a/CHANGES.rst b/CHANGES.rst index 9e3ea6e2..774f8c5b 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -23,7 +23,7 @@ Bug fixes: - Fixes test to work clean with zope.interface. - Interfaces are hashed based on just their name and module. + Interfaces are hashed based on just their name and module. So every one of these local `IBlank` interfaces will hash the same way, and be treated the same for purposes of zope.interface's `_dependents`. Thus in tests mock interfaces must not be used under the same name in the same module. [jensens] (#135) @@ -179,9 +179,6 @@ New features: Bug fixes: -- Fix ft._updateProperty so it doesn't break when receiving an empty value. - This happens when an DX FTI is part of a Generic Setup baseline import. - [jaroel] - Other Python 3 compatibility fixes [ale-rt, pbauer, jensens] diff --git a/news/85.bugfix b/news/85.bugfix new file mode 100644 index 00000000..e1e0943d --- /dev/null +++ b/news/85.bugfix @@ -0,0 +1,3 @@ +Fix ft._updateProperty so it doesn't break when receiving an empty value. +This happens when an DX FTI is part of a Generic Setup baseline import. +[jaroel] From 805c92a9d4df055b8a83d01e294ac00a05de4308 Mon Sep 17 00:00:00 2001 From: ale-rt <alessandro.pisa@gmail.com> Date: Wed, 10 Feb 2021 15:22:19 +0100 Subject: [PATCH 27/29] Remove commented code --- plone/dexterity/content.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/plone/dexterity/content.py b/plone/dexterity/content.py index dcb619f3..433c1edd 100644 --- a/plone/dexterity/content.py +++ b/plone/dexterity/content.py @@ -779,15 +779,6 @@ def __getattr__(self, name): raise AttributeError(name) - # XXX do we really want to handle this kind of cases, or is this a ID-10-T - # thingy? - # def __setattr__(self, name, obj): - # if self._tree is not None and name in self: - # # We're trying to set an item via dotted name... - # del self[name] - # self[name] = obj - # super(Container, self).__setattr__(name, obj) - @security.protected(permissions.DeleteObjects) def manage_delObjects(self, ids=None, REQUEST=None): """Delete the contained objects with the specified ids. From d5a68965255a0bce3770ced6a725fcdf3b798a30 Mon Sep 17 00:00:00 2001 From: Maurits van Rees <maurits@vanrees.org> Date: Wed, 18 Aug 2021 14:52:47 +0200 Subject: [PATCH 28/29] Use queryUtility to get Plone Site dexterity FTI in portalTypeToSchemaName. That makes this code usable when Plone Site is not (yet) a dexterity item. I doubt that this code path is ever reached when the Plone Site FTI is not dexterity, but let's be careful. --- plone/dexterity/schema.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plone/dexterity/schema.py b/plone/dexterity/schema.py index 22e6b18e..7a0ef351 100644 --- a/plone/dexterity/schema.py +++ b/plone/dexterity/schema.py @@ -324,10 +324,12 @@ def split(self, s): def portalTypeToSchemaName(portal_type, schema=u"", prefix=None, suffix=None): """Return a canonical interface name for a generated schema interface.""" if prefix is None: + siteroot = None if portal_type == "Plone Site": - fti = getUtility(IDexterityFTI, name=portal_type) - siteroot = fti.__parent__ - else: + fti = queryUtility(IDexterityFTI, name=portal_type) + if fti is not None: + siteroot = fti.__parent__ + if siteroot is None: siteroot = getUtility(ISiteRoot) prefix = "/".join(siteroot.getPhysicalPath())[1:] if suffix: From 1fbd4947c309aa8200b33547ea8667e7a62f115e Mon Sep 17 00:00:00 2001 From: Maurits van Rees <maurits@vanrees.org> Date: Wed, 18 Aug 2021 14:54:27 +0200 Subject: [PATCH 29/29] More info in news snippet. --- news/85.bugfix | 1 + 1 file changed, 1 insertion(+) diff --git a/news/85.bugfix b/news/85.bugfix index e1e0943d..e690b818 100644 --- a/news/85.bugfix +++ b/news/85.bugfix @@ -1,3 +1,4 @@ Fix ft._updateProperty so it doesn't break when receiving an empty value. This happens when an DX FTI is part of a Generic Setup baseline import. +Update more code to work when the Plone Site is a dexterity item. [jaroel]