how to Removing broken product references

Removing broken product references

Removing a persistent local utility part II

http://blog.fourdigits.nl/removing-a-persistent-local-utility-part-ii

The Problem

You have created a Plone 4 site but then you load your old Plone 3 Data.fs you are plagued with errors of the form:

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

The problem is that products fail to uninstall themselves properly. This appears to be endemic up to and including Plone 4.0.3. The ZopeSkel trees created by paster do not contain sample uninstall information. Therefore, if you add a product, use it then remove it, your database is now infested with references to these unused products.

They have to go.

Internal Consistency

There are a couple of interfaces in the ZMI which you can use:

portal_catalog

Go to:

  1. the ZMI
  2. portal_catalog
  3. Indexes tab
  4. select meta_type

and check it's not being used.

Re-indexing the Database

The database can be re-indexed, though it doesn't seem to make much difference in with this problem.

Go to:

  1. the ZMI
  2. portal_catalog
  3. Advanced tab
  4. Clear and rebuild
Packing the Database

The database can be packed, though again it doesn't seem to make much difference in with this problem.

Go to:

  1. the ZMI of the main Plone system, ie. http://hostname:port (for the port of client1)
  2. Control Panel
  3. Database Management
  4. main
  5. Pack

A Clean Plone System

Far and away the easiest solution is to set up another Plone 3 system in parallel with your first, identical in every (possible) way with your original only without any unrequired products and attempt to migrate you original Data.fs into the parallel Plone. Once you have removed the problems within a Plone 3.3.5 system you are much further towards being able to migrate your database into a Plone 4 system.

buildout

It is entirely possible that the act of running buildout makes your Plone 3.3.5 system unusable. buildout may start to reference newer versions of, say, setuptools which conflict with existing versions of products.

You can pin products to specific versions in buildout.cfg, eg.:

PIL==1.1.6

except there's no obvious way of doing that with setuptools. Even the [versions] section doesn't seem to work.

There is no clear solution to this.

Fixing __iro__ problems

It takes a lot of googling but you can eventually track down articles such as from Plone themselves and others where you can derive the following set of actions.

These actions should be performed on your original good database. You do have a backup, don't you?

Each time through these actions you'll be removing another unwanted class then you'll try migrating the database into your parallel Plone 3 system. If you get another __iro__ problem then you'll have to re-run through these actions on the original database again.

  1. try migrating Data.fs into the parallel Plone 3
  2. fix original Plone 3 to remove the bad class
  3. repeat

Determine the Offending Class

In our case:

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

the class is clearly IAddressBookUtility.

Check its not in Use

Check through the portal_catalog as described above.

If it is, you're on your own. Sorry.

Find its Package

We'll have to grep around in the Product in the original Plone's buildout-cache/eggs area:

% cd .../buildout-cache/eggs
% find . -name \*.py | xargs grep IAddressBookUtility | grep import
./Products.PloneGetPaid-0.8.8-py2.4.egg/Products/PloneGetPaid/generations/r30.py:from Products.PloneGetPaid.interfaces import IAddressBookUtility
...

Ah, yes, we played about with some shopping cart stuff, we'd forgotten about that.

The key part is we now know where to import the class from, Products.PloneGetPaid.interfaces.

The Removal Script

Yes, you need to type this out by hand or have it generated for you:

sm = app.*your-plone-site*.getSiteManager()
from Products.PloneGetPaid.interfaces import IAddressBookUtility
i=IAddressBookUtility
u = sm.getUtility(i)
print u
if u:
   sm.unregisterUtility(provided=i)
   del u
   sm.utilities.unsubscribe((), i)
   del sm.utilities.__dict__['_provided'][i]
   del sm.utilities._subscribers[0][i]
   del sm.utilities._adapters[0][i]
   str(sm.utilities._adapters[0]).find('AddressBook')
   str(sm.utilities._subscribers[0]).find('AddressBook')
   str(sm.utilities.__bases__[0].__dict__).find('AddressBook')
   import transaction
   transaction.commit()
   app._p_jar.sync()

Where the string AddressBook is plucked out of the air by you and is used as a means to search for likely sounding instances of the class. Hopefully, you'll end up with -1 search instances, ie. you've successfully removed the class.

Note

The value for *your-plone-site* is that same as the URL used by Plone when you created your Plone site. If you access the Plone server directly, ie. http://hostname:port then you'll see the Plone sites as elements in that view. Use the name of the element as you see it there.

The meat of this script is to:

  1. get the SiteManager object
  2. get the Utility associated with the problematic class
  3. unregister the class -- adding provided= seemed to fix one version of the script found online
  4. delete the Utility object
  5. delete the class from various tables where it seems it gets left behind (__dict___subscribers and _adapters)
  6. double-check by running a couple of searches
  7. finally, commit the database back out!

Running the Removal Script

This bit isn't immediately obvious but you can run individual Plone clients in debug mode, ie. with a Python prompt:

% ./bin/client1 stop
% ./bin/client1 debug

eventually you'll get a Python prompt ( >>> ) with a message that the top-level application is available in the variable app (which we use immediately to get hold of the SiteManager object)

Having run the script, quit the Python instance ( ^D ) and restart the client:

% ./bin/client1 start