Ticket #10318 (closed Bug: fixed)

Opened 4 years ago

Last modified 4 years ago

ExtensionClass change causes "Attempt to set attribute on empty acquisition wrapper"

Reported by: newbery Owned by: hannosch
Priority: blocker Milestone: 4.0
Component: General Version:
Keywords: Cc: frisi, esteele

Description

Using 4.0b1,

It seems that any TTW view customization via /portal_view_customizations causes later views to throw the following error:

2010-03-10 23:10:40 ERROR ZODB.Connection Couldn't load state for 0x1c44
Traceback (most recent call last):
  File ".../ZODB3-3.9.4-py2.6-macosx-10.3-i386.egg/ZODB/Connection.py", line 838, in setstate
    self._setstate(obj)
  File ".../ZODB3-3.9.4-py2.6-macosx-10.3-i386.egg/ZODB/Connection.py", line 906, in _setstate
    self._reader.setGhostState(obj, p)
  File ".../ZODB3-3.9.4-py2.6-macosx-10.3-i386.egg/ZODB/serialize.py", line 616, in setGhostState
    state = self.getState(pickle)
  File ".../ZODB3-3.9.4-py2.6-macosx-10.3-i386.egg/ZODB/serialize.py", line 609, in getState
    return unpickler.load()
  File ".../ZODB3-3.9.4-py2.6-macosx-10.3-i386.egg/ZODB/serialize.py", line 477, in _persistent_load
    return self.load_persistent(*reference)
  File ".../ZODB3-3.9.4-py2.6-macosx-10.3-i386.egg/ZODB/serialize.py", line 514, in load_persistent
    obj._p_oid = oid
AttributeError: Attempt to set attribute on empty acquisition wrapper

2010-03-10 23:10:40 ERROR Zope.SiteErrorLog 1268291440.780.276724844509 http://localhost:8888/Plone2
Traceback (innermost last):
  Module ZPublisher.Publish, line 116, in publish
  Module ZPublisher.BaseRequest, line 434, in traverse
  Module ZPublisher.BeforeTraverse, line 99, in __call__
  Module Products.CMFCore.PortalObject, line 81, in __before_publishing_traverse__
  Module Products.CMFCore.DynamicType, line 142, in __before_publishing_traverse__
  Module zope.publisher.defaultview, line 88, in queryDefaultViewName
  Module ZODB.Connection, line 838, in setstate
  Module ZODB.Connection, line 906, in _setstate
  Module ZODB.serialize, line 616, in setGhostState
  Module ZODB.serialize, line 609, in getState
  Module ZODB.serialize, line 477, in _persistent_load
  Module ZODB.serialize, line 514, in load_persistent
AttributeError: Attempt to set attribute on empty acquisition wrapper


Similar tb's actually show up multiple times but I'm guessing that's just some error cascading artifact.

Restarting the instance does not eliminate the error.

Patching line 514 in ZODB.serialize.py like so:

        try:
            obj._p_oid = oid
        except AttributeError:
            return self._conn.get(oid)

seems to fix the issue for now but I have no idea if this solution is robust enough for the long term.

Change History

comment:1 Changed 4 years ago by kleist

  • Component changed from Unknown to Infrastructure

comment:2 Changed 4 years ago by hannosch

  • Owner set to hannosch

I have seen the same issue in a Plone 3.x site after upgrading to the new Acquisition/ExtensionClass releases. In that case it only happened with the singing&dancing signup portlet. I haven't debugged this yet.

comment:3 Changed 4 years ago by frisi

  • Cc frisi added

got the same error when adding a p4a.subtype for collective.lineage (see  http://plone.org/products/collective-lineage/issues/15) the patch above works around the problem

comment:4 Changed 4 years ago by hannosch

  • Summary changed from TTW view customization throws error to Attempt to set attribute on empty acquisition wrapper

I debugged this a bit more. What happens is that some persistent references end up being of the form ('0x001', 'Acquisition.ImplicitAcquisitionWrapper')

This causes the _persistent_load method to try to load the oid, with the ImplicitAcquisitionWrapper as the class. Loading the actual object and ignoring the class from the reference works fine. So at least there's no unrecoverable data loss caused by this.

My workaround for this looks like this for now:

def _persistent_load(self, reference):
    if isinstance(reference, tuple):
        # Ignore broken AQ wrapper hint
        if reference[1] == ImplicitAcquisitionWrapper:
            return self.load_oid(reference[0])
        return self.load_persistent(*reference)
    elif isinstance(reference, str):
        return self.load_oid(reference)
...

Of course the real bug is the presence of the ImplicitAcquisitionWrapper in the reference tuple. This shouldn't be there in the first place. Instead the actual class should be stored there.

comment:5 Changed 4 years ago by frisi

maybe this helps in tracking down where the real problem occures:

i got the "Attempt to set attribute on empty acquisition wrapper" error when marking a folder with p4a.subtyper (see  this lineage issue)

using a version of plone.app.folder that does not contain the change made in  r34308 made the error go away.

comment:6 Changed 4 years ago by davisagli

The storage of that <ImplicitAcquisitionWrapper> reference in the persistent reference is a side effect of the  change in ExtensionClass to no longer provide a getnewargs method.

From ObjectWriter.persistent_id in ZODB/serialize.py:

        klass = type(obj)
        if hasattr(klass, '__getnewargs__'):
            # We don't want to save newargs in object refs.
            # It's possible that __getnewargs__ is degenerate and
            # returns (), but we don't want to have to deghostify
            # the object to find out.

            # Note that this has the odd effect that, if the class has
            # __getnewargs__ of its own, we'll lose the optimization
            # of caching the class info.

            if database_name is not None:
                return ['n', (database_name, oid)]

            return oid

        # Note that we never get here for persistent classes.
        # We'll use direct refs for normal classes.

        if database_name is not None:
            return ['m', (database_name, oid, klass)]

        return oid, klass

comment:7 Changed 4 years ago by frisi

pinning ExtensionClass = 2.12.0 in my buildout works around this error for new objects.

an object stored using 2.13 that produced the AttributeError can't be fixed with that. for these cases one needs  the fix proposed in comment 4

is ist wiser/safer to pin ExtensionClass, or to patch serialize.py? will i lose the performance gain for objects created with 2.12?

comment:8 follow-up: ↓ 10 Changed 4 years ago by davisagli

  • Priority changed from major to blocker
  • Cc esteele added
  • Summary changed from Attempt to set attribute on empty acquisition wrapper to ExtensionClass change causes "Attempt to set attribute on empty acquisition wrapper"

This is a blocker for 4.0b2, IMO.

I think the change in ExtensionClass is also the culprit for the PicklingErrors some people have been seeing during upgrading, if there are items whose class can't be imported -- see e.g.  http://n2.nabble.com/Upgrade-to-Plone-4-td4758292.html

frisi, presumably if you re-pickled all your objects you could then remove the serialize.py patch again. Yes, you will lose the performance gain from the ExtensionClass change.

comment:9 Changed 4 years ago by hannosch

  • Status changed from new to closed
  • Resolution set to fixed

(In [35659]) Use new Acquisition 2.12.2 release, this closes #10318.

comment:10 in reply to: ↑ 8 Changed 4 years ago by frisi

Replying to davisagli:

frisi, presumably if you re-pickled all your objects you could then remove the serialize.py patch again. Yes, you will lose the performance gain from the ExtensionClass change.

thx for your answer. what is the easiest way to re-pickle objects to get rid of this patch in an existing instance? is there some utility script or method i can call in pdbdebugmode or zopepy?

comment:11 Changed 22 months ago by davisagli

  • Component changed from Infrastructure to General
Note: See TracTickets for help on using tickets.