Cleaning up a wicked mess

I'm in the process of doing practice run migrations for several of our sites from Plone 4.1.6 to Plone 5.0.5. Many of these sites have been around since Plone 3 or maybe even earlier, which means they've acquired some vestigial components that might not have uninstalled completely. The problem does not always materialize until later when the package of antiquated modules is finally removed, causing them to break. That's what I think I am dealing with in this transition - lots of problems with the wicked package, and to my knowledge we never even used it. This post will maybe serve as a reference to others that might have this problem, but I hope also to pick the brains of people that have a good knowledge of low level zope component architecture.

Ordinarily I believe the preferred means of removing problem components is to reinstall the removed package and use its uninstall methods. That doesn't really work here - for one thing, wicked 1.1.9 doesn't even have an install method, just zcml, and I don't know what junk was put in the site from previous versions. So I'm attempting to solve this the hard way, by directly manipulating some zope component architecture elements.

First, let me give a quick overview of my setup. My production sites in 4.1.6 remain untouched, I just make a zexp of them and download them locally. I have two environments set up on my local machine: 4.1.6 and 5.0.5.

  1. Plone 4 - run custom update to clean up some things
  2. move filestorage and blobstorage dirs to Plone 5 environment
  3. Plone 5 - run custom update for more clean up, that relies on Plone 5 eggs
  4. Plone 5 - run Plone's normal migration
  5. Plone 5 - run custom update to apply all of our custom configurations and run AT->Dexterity conversion for our custom content (installing plone.app.contenttypes and migrating base content from AT->Dexterity is also baked into this)

The wicked package has caused me the most headaches, with the worst offender being trying to access just the root of the portal in the ZMI after starting up Plone 5. I originally got this error:

Traceback (innermost last):

Module ZPublisher.Publish, line 249, in publish_module_standard
Module ZPublisher.Publish, line 197, in publish
Module zope.event, line 31, in notify
Module zope.component.event, line 24, in dispatch
Module zope.component._api, line 136, in subscribers
Module zope.component.registry, line 320, in subscribers
Module ZODB.Connection, line 860, in setstate
Module ZODB.Connection, line 914, in _setstate
Module ZODB.serialize, line 613, in setGhostState
Module zope.component.persistentregistry, line 40, in __setstate__
Module zope.interface.adapter, line 91, in _createLookup
Module zope.interface.adapter, line 439, in __init__
Module zope.interface.adapter, line 476, in init_extendors
Module zope.interface.adapter, line 480, in add_extendor

AttributeError: type object 'IFieldValueSetter' has no attribute '__iro__' 

As I said, I think the origin of the problem was a bad cleanup of wicked from an earlier Plone version that left some orphans. Plone 5 no longer has the wicked package, and the leftover pieces cause the whole thing to crash. I could reproduce the same error message in Plone 4.1.6 by removing wicked from the buildout and starting up. To fix this I added the following to my custom update in Plone 4:

from zope.component import getSiteManager
from wicked.fieldevent.interfaces import IFieldValueSetter

adapter_registry = getSiteManager().adapters
if IFieldValueSetter in adapter_registry._provided:
      del adapter_registry._provided[IFieldValueSetter]
      adapter_registry._v_lookup.remove_extendor(IFieldValueSetter)
      adapter_registry.changed(adapter_registry)

Ideally I would have run adapter_registry.unregister or even better, unregister it through a profile step with an XML file. i did not have success for either of these approaches, so I did my best to approximate what is happening in adapter_registry.unregister. If anyone has better knowledge here and can provide insight that would be very helpful.

I had one other issue when trying to reimport it back into a Plone 5 environment on a dev server: it told me it was unable to find WickedSettings when unpickling. I always find these errors frustrating because I don't know where in the portal it supposedly is. With a broken content object I can see it in the ZMI, but not with this. I eventually found it was an annotation and removed it.

bad_annotations = ('plone.app.controlpanel.wicked',)
for bad_annotation in bad_annotations:
    if bad_annotation in IAnnotations(portal):
        del IAnnotations(portal)[bad_annotation]

I'm still in the process of testing that these practice runs do everything we expect of them, but for now things are looking good again. Apologies if you were a contributor to wicked :slight_smile:

Have you tried https://pypi.org/project/wildcard.fixpersistentutilities ? I'm not sure if it will work here, but it's worth a shot.

1 Like

At this least, this looks like it would be a handy inspection tool. I did not know about it, thanks. It doesn't seem to be working for me when I actually try to delete anything but I suppose that is for another topic.

What happens when you try to delete something?

I took a look at our docs and this is the place I found a mention of wildcard.fixpersistentutilities. It needs more love

https://docs.plone.org/manage/troubleshooting/manual-remove-utility.html

Issue filed: https://github.com/plone/documentation/issues/799

Here is the traceback when trying to delete just about anything, either on the gsm or a local sm for browserlayer. This has happened in both Plone 4.1 and 5.0

Traceback (innermost last):

  Module ZPublisher.Publish, line 60, in publish
  Module ZPublisher.mapply, line 77, in mapply
  Module ZPublisher.Publish, line 46, in call_object
  Module wildcard.fixpersistentutilities.views, line 116, in delete_utility_reg
  Module zope.dottedname.resolve, line 38, in resolve

ImportError: No module named InterfaceClass 

That class is from Acquistion so it looks like it's trying to resolve the acquisition wrapper as part of the dotted name. Probably just need to get the aq_base first.

Also if I simply remove the wicked package, that fixpersistentutilities page does not show anything for wicked in the global site manager. But if I view that locally on a site I am trying to migrate, I get "AttributeError: type object 'IFieldValueSetter' has no attribute 'iro'" like I mentioned above.

My guess is that at some point wicked used to register this locally and later changed it to a global setting. Perhaps the zope architecture ended up working in such a way that it would fall back to the global registration, and you could then unregister it from the global site manager, but if you didn't have the global registration to begin with it would just say the local adapter is invalid? I can't confirm this.

Because I dealt with this multiple times already I created a small addon that mocks the remains of wicked in a migrated database:


It mocks the IFieldValueSetter and selective-atct.zcml in wicked.plone without doing anything. You can then remove wicked from your buildout.
4 Likes