quick search:
 

Those #$%@ Stubborn POSKeyErrors

Submitted by: heureso
Last Edited: 2007-04-24

Category:

Average rating is: 5.0 out of 5 (1 ratings)

Description:
This is an update to an earlier recipe (http://www.zopelabs.com/cookbook/1054240694) for fixing the dreaded POSKeyError. This recipe is updated for Zope 2.7 and higher, using the 'zopectl debug' utility instead of directly invoking the Python interpreter. Using the debugger in this way automagically sets the correct PYTHONPATH and binds the Zope root object to app.

Source (Text):
1. From the Zope bin directory (frequently /usr/local/zope/bin on UNIX machines), invoke zopectl:

[user@machine bin]$ ./zopectl

2. From the zopectl prompt, stop your Zope instance:

zopectl> stop

3. From the zopectl prompt, start the debugging interpreter:

zopectl> debug

4. From the debug prompt, do a little bit of setup:

>>> from Zope.Startup.run import configure
>>> configure('/path/to/zope.conf')
>>> from ZODB.POSException import POSKeyError

For the second line above, put in the full filesystem path to your zope.conf file. For many Linux installations, this will be '/usr/local/zope/etc/zope.conf'.

5. Get at the Zope path for the container of the object that's throwing the POSKeyError:

>>> obj=root.unrestrictedTraverse('/path/to/container')

6. Find the ID of the object that's causing the problem:

>>> for id,val in obj.objectItems():
...     try: val.getId()
...     except POSKeyError: break
...

This should produce a list of all the objects in the containing folder stopping at the one that throws the POSKeyError exception. The id of that object will now be bound to the variable id. You can find the id of the cursed, evil, broken object:

>>> id

'nameOfBrokenObject'

7. In the previous recipe, this is where we would have called the manage_delObjects method of the containing folder, but in solving my own POSKeyError, this didn't work any more. I'm sure someone more enlightened than I could tell you why, but I found (with much Googling) another way. In all frankness, the mystical workings of the following code are over my head, but it works. The gist is that it removes "the reference from the parent folder, without accessing the
bad object." This gem was found in the following list thread: http://www.dzug.org/mailinglisten/zope-org-zope/archive/2004/2004-07/1090024676313

>>> obj._objects=tuple(filter(lambda i,n=id: i['id']!=n, obj._objects))
>>> delattr(obj,id)

8. Finally, you must commit the transactions to ZODB:

>>> get_transaction().commit()

9. You can now exit from the debugger with Ctrl-D or exit:

>>> exit

10. And restart your Zope instance:

zopectl> start

If your Zope instance doesn't restart cleanly, you may have more than one bad object, so you may need to repeat the above steps.

11. And quite out of zopectl:

zopectl> exit


N.B.: In more recent versions of Zope (beginning, I believe, with 2.9) you now need to handle step 8 differently:

>>> import transaction
>>> transaction.commit()

Explanation:
The target audience for this recipe is anyone afflicted by a POSKeyError in Zope >= 2.7.

The recipe guides you through directly editing Zope's ZODB database to delete an errant reference to a damaged or missing object.

Thanks go out, once again, to Dieter Maurer <dieter at handshake.de> for helping me through my first POSKeyError and to Roché Compaan <roche at upfrontsystems.co.za>, who suggested the deeply mysterious magic in step 7.



Comments:

by slinkp - 2004-11-30
The filtering code isn't so scary as all that :-)
If you're not familiar with lambda and filter, note that the following list comprehension should have the same effect (untested):

obj._objects=tuple([o for o in obj._objects if o['id'] != n])


root.unrestrictedTraverse not working. by ivaldes1 - 2005-12-29
This line of the above recipe doesn't work.

>>> obj=root.unrestrictedTraverse('/linuxmednews')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
NameError: name 'root' is not defined
 
Re: root.unrestrictedTraverse not working. by ivaldes1 - 2005-12-29
My localhost installation was looking for 'app' which it listed at the beginning of the session. So it should be:

obj=app.unrestrictedTraverse('/linuxmednews')

Thanks!

-- IV
-- http://www.linuxmednews.com