Examples, tips
Invoking the python debugger
Here are two ways to get into zope with the python debugger.
interactive prompt
This is the most flexible. Use Zope 2.7's zopectl debug command
(there's a way to do this with older versions too). You must first shut
down your zope server, unless you are DebuggingWithZEO. This brings up
zope with an interactive python prompt. Now you can use python's
debugger, eg:
>>> import pdb
>>> pdb.run('app.zopewiki.ZopeWiki.wikiSubscriberCount()')
> <string>(1)?()
(Pdb)
If you're DebuggingWithEmacs, a second window showing source code should appear at this point.
breakpoint in the code
Though it requires a code change, this is sometimes the quickest way to get to the heart of a complex context. Insert:
import pdb pdb.set_trace()
into the code where you want a breakpoint. Now restart Zope
(or start a new ZEO client) with zopectl fg,
so that the debug prompt will appear on your console. With older Zope <2.8.1
you might need to add -Xdebug-mode=on or use runzope. Or, if your running
zope is already in debug mode, just a ProductRefresh? may be enough. Now reload the
webpage that triggers the bug you are trying to find. When the above
code is reached Zope will stop and give you a pdb debug prompt.
If you want to break only under certain circumstances, simply insert an
if before the code: if iter == 25: import pdb;pdb.set_trace(). Can
you imagine a more obvious way to set conditions on breakpoints? Python
rocks!
Exploring the namespace
app is the root of the ZODB. You can dig around with dir(app), help(app),
app.objectIds() and so on.
ZODB object loading
When you start debugging, you may find yourself in __setstate__ or __getattr__ instead of the code you tried to debug. This is just Zope loading a ZODB object into memory for the first time. Return out of there and continue:
>>> pdb.run('app.zopewiki.ZopeWiki.wikiSubscriberCount()')
> <string>(1)?()
(Pdb) s
--Call--
> /zope1/Products/BTreeFolder2/BTreeFolder2.py(472)__getattr__()
-> def __getattr__(self, name):
(Pdb) r
--Return--
> /zope1/Products/BTreeFolder2/BTreeFolder2.py(480)__getattr__()-><ZWikiPa...ZopeWiki>
-> return res
(Pdb) s
--Call--
> /zope1/Products/ZWiki/Mail.py(245)wikiSubscriberCount()
-> def wikiSubscriberCount(self):
(Pdb)
Debugging with ZPublisher
Calling methods directly with pdb.run works for simple methods which don't require the normal ZPublisher context - authentication, a REQUEST, etc. If you run into problems, you'll need to use Zope.debug instead (an alias for a ZPublisher test method). Eg:
>>> Zope.debug('/full/path/to/object')
The full folder path is required even if you have configured a virtual host. Here's an example with authentication and debugging; see the docs for more Zope.debug options .:
>>> Zope.debug('/zopewiki/ZopeWiki/wikiSubscriberCount',u='user:pass',d=1)
* Type "s<cr>c<cr>" to jump to beginning of real publishing process.
* Then type c<cr> to jump to the beginning of the URL traversal
algorithm.
* Then type c<cr> to jump to published object call.
> <string>(1)?()
pdb> s
--Call--
> /usr/local/src/Zope-2.7.0/lib/python/ZPublisher/Test.py(173)publish_module()
-> def publish_module(module_name,
pdb>
Convenient breakpoints
Zope.debug sets some convenient breakpoints to let you bypass ZPublisher code and get to yours quicker. Normally, s, c, c, s gets you there. Unfortunately this seems not to work in zope 2.7. Here's one way to set one of these breakpoints and use it to step into your code:
pdb> b ZPublisher/Publish.py:40
Breakpoint 3 at /usr/local/src/Zope-2.7.0/lib/python/ZPublisher/Publish.py:40
pdb> c
> /usr/local/src/Zope-2.7.0/lib/python/ZPublisher/Publish.py(40)call_object()
-> result=apply(object,args) # Type s<cr> to step into published object.
pdb> s
--Call--
> /usr/local/src/Zope-2.7.0/lib/python/OFS/DTMLMethod.py(98)__call__()
-> def __call__(self, client=None, REQUEST={}, RESPONSE=None, **kw):
pdb>
Breakpoint problems
But in fact, c in a zope debug session frequently ignores breakpoints. Why is this ?
Persistence of changes
If i worked through a session with the debugger and did some changes to objects, are these changes really done permanently? I changed some props of an AudioObject?, and i got the expected final ZPT at the end (but an error in the middle), so i thought the changes are saved (transactions were commited). But when i started Zope as usual again later i checked the object and there were no changes at all. Why??
To commit changes made in an interactive session to the zope database, use getTransaction().commit().
To discard your changes and revert to the current ZODB contents instead, do app._p_jar.sync().
Python class names and object paths
Let's say that you have a News object which is defined in a ZopeProduct
called ZopeNews?, and is located in the INSTANCE_HOME/Products/ZopeNews?
directory. The class that defines the News instance is also called News,
and is defined in the News.py module in your product. Therefore, from
Zope's perspective the fully qualified name of your class is
Products.ZopeNews.News.News.
Say we want to call the method postnews in class News. Why is the path to submit to ZPublisher in this case:
Zope.debug('/News/postnews?new=blah', d=1)
Answer: you are not debugging a class, but an object which is an
instance of that class. We are in instance space, defined by the
ZODB's folder hierarchy, not class space, defined in the python source code.
Your object is sitting in zope's
root folder and happens to have the same id as its class (News).
Multiple thread problem
(Applies to Zope 2.6 interactive prompt only ? ConversingWithZope? explains this.) Avoid c/continue. Python will bring you back to the debug prompt for this thread with the next request, and you'll end up having several threads running in the debugger at one time. VERY confusing. Use n/next and s/stepin, and when you are done debugging one request, quite the debugger with q/quit. This will either leave your browser hanging, or give you a pdb.Quit exception, but that's not a problem.