[Zope-dev] The Debugger Is Your Friend - an object Browser would be nice too

Loren Stafford lstaffor@dynalogic.com
Mon, 28 Feb 2000 16:00:35 -0800


Does anyone know of such a thing as a generic Python class browser that
could be adapted to Zope?

-- Loren

----- Original Message -----
From: Damian Morton <morton@dennisinter.com>
To: Michel Pelletier <michel@digicool.com>
Cc: <zope-dev@zope.org>
Sent: February 28, 2000 03:26 PM
Subject: RE: [Zope-dev] The Debugger Is Your Friend - an object Browser
would be nice too


> An extremely usefull adjunct to the debugger would be an object browser
for
> the running Zope application.
>
> Unfortunately, using the monitor or even the debugger to browse through
the
> running application can be extremely painful, especialy as many of the
> methods and properties of objects arent displayed using dir(object) or
> object.__dict__. What is needed is a browser that understands classes,
> inheritance and acquisition.
>
> Im looking to find the list of properties associated with the object
> StuffMag.
>
> Heres how Im using the monitor to browse the live application:
>
> ### lets bring in something to help format our data
> >>> from pprint import pprint
>
> ### This is the object I want to examine
> >>> app.StuffMag
> <StuffClass instance at 011FF190>
>
> #### Lets see whats inside this object
> >>> pprint(app.StuffMag.__dict__)
> {'BabeSection': <StuffSectionClass instance at 01222720>,
>  'OtherSection': <StuffSectionClass instance at 01222370>,
>  'StuffSection': <StuffSectionClass instance at 012227B0>,
>  '__ac_local_roles__': {'damien.morton': ['Owner']},
>  '_objects': ({'meta_type': 'Section object', 'id': 'BabeSection'},
>               {'meta_type': 'Section object', 'id': 'StuffSection'},
>               {'meta_type': 'Section object', 'id': 'OtherSection'},
>               {'meta_type': 'Section2 object', 'id': 'section2'},
>               {'meta_type': 'Section3 object', 'id': 'section3'},
>               {'meta_type': 'Section5 object', 'id': 'section5'},
>               {'meta_type': 'Section5 object', 'id': 'section5.2'},
>               {'meta_type': 'Section5 object', 'id': 'mysection5'},
>               {'meta_type': 'myClass object', 'id': 'myObject'}),
>  'body': '',
>  'heading': 'HI THERE!!!',
>  'id': 'StuffMag',
>  'myObject': <myClass instance at 01222660>,
>  'mysection5': <Section5Class instance at 012222A0>,
>  'section2': <StuffSection2 instance at 012224D0>,
>  'section3': <section3Class instance at 012225A0>,
>  'section5': <Section5Class instance at 012218A0>,
>  'section5.2': <Section5Class instance at 01222400>}
>
> >>> app.StuffMag.propertysheets
> <StuffClass_PropertySheetsClass instance at 011FAA50>
>
> ### How did I know this object had a propertysheets attribute?
> >>> pprint(app.StuffMag.__class__.__dict__)
> {'Section5Class': <MWp instance at 011FACE0>,
>  'Section5Class_add': <MWp instance at 011FA4E0>,
>  'Section5Class_addForm': <MWp instance at 011FA8B0>,
>  'Section5Class_factory': <MWp instance at 011FA4C0>,
>  'StuffSection2': <MWp instance at 011F9E30>,
>  'StuffSection2_add': <MWp instance at 011C57C0>,
>  'StuffSection2_addForm': <MWp instance at 011F9140>,
>  'StuffSection2_factory': <MWp instance at 011FA3C0>,
>  'StuffSectionClass': <MWp instance at 011FAF60>,
>  'StuffSectionClass_add': <MWp instance at 011FABE0>,
>  'StuffSectionClass_addForm': <MWp instance at 011C71C0>,
>  'StuffSectionClass_factory': <MWp instance at 011FA530>,
>  '__ac_permissions__': (('Add Section objects', (), ('Manager',)),),
>  '__doc__': 'StuffClass',
>  '__module__': '*gK7YKWqDX9Dn00XJH1ZJPQ==',
>  '_p_changed': None,
>  '_p_jar': <ZODB.Connection.Connection instance at 11ec6a0>,
>  '_p_oid': '\000\000\000\000\000\000\014Q',
>  '_p_serial': '\0032\254\306`\335\272\346',
>  '_zclass_method_meta_types': ({'action': 'StuffSectionClass_factory',
>                                 'name': 'Section object',
>                                 'permission': 'Add Section objects',
>                                 'product': 'methods'},
>                                {'action': 'Section5Class_factory',
>                                 'name': 'Section5 object',
>                                 'permission': 'Add Section objects',
>                                 'product': 'methods'},
>                                {'action': 'StuffSection2_factory',
>                                 'name': 'Section2 object',
>                                 'permission': 'Add Section objects',
>                                 'product': 'methods'},
>                                {'action': 'section3Class_factory',
>                                 'name': 'Section3 object',
>                                 'permission': 'Add Section objects',
>                                 'product': 'methods'},
>                                {'action': 'myClass_factory',
>                                 'name': 'myClass object',
>                                 'permission': 'Add myClass objects',
>                                 'product': 'methods'}),
>  'body': '',
>  'heading': '',
>  'icon': '',
>  'isPrincipiaFolderish': 'Y',
>  'manage_options': ({'action': 'propertysheets/Properties/manage',
>                      'label': 'Properties'},
>                     {'label': 'Contents', 'action': 'manage_main'}),
>  'meta_type': 'Stuff Site object',
>  'meta_types': (),
>  'myClass': <MWp instance at 011F9EA0>,
>  'myClass_add': <MWp instance at 011C8210>,
>  'myClass_addForm': <MWp instance at 011F9430>,
>  'myClass_factory': <MWp instance at 011FA740>,
>  'propertysheets': <StuffClass_PropertySheetsClass instance at 011FAA50>,
>  'section3Class': <MWp instance at 011B2960>,
>  'section3Class_add': <MWp instance at 011FAE60>,
>  'section3Class_addForm': <MWp instance at 011F8230>,
>  'section3Class_factory': <MWp instance at 011F92C0>}
>
> ### delving even deeper
> >>> for b in app.StuffMag.__class__.__bases__:
> ...     pprint(b.__dict__)
>
> {'__class_init__': <function __class_init__ at a05bf0>,
>  '__module__': 'ZClasses.ZClass'}
> {'__module__': 'ZClasses.ObjectManager',
>  '__roles__': <PermissionRole instance at 00986D50>,
>  '_zclass_method_meta_types': (),
>  'all_meta_types': <function all_meta_types at a6ca90>,
>  'manage_FTPlist__roles__': <PermissionRole instance at 009D7F30>,
>  'manage_FTPstat__roles__': <PermissionRole instance at 009D7F30>,
>  'manage_delObjects__roles__': <PermissionRole instance at 009D4FF0>,
>  'manage_exportObject__roles__': <PermissionRole instance at 00A4D890>,
>  'manage_importExportForm__roles__': <PermissionRole instance at
00A4D890>,
>  'manage_importObject__roles__': <PermissionRole instance at 00A4D890>,
>  'manage_main__roles__': <PermissionRole instance at 00980DF0>,
>  'manage_menu__roles__': <PermissionRole instance at 00980DF0>,
>  'meta_types': (),
>  'objectIds__roles__': <PermissionRole instance at 00986D50>,
>  'objectItems__roles__': <PermissionRole instance at 00986D50>,
>  'objectValues__roles__': <PermissionRole instance at 00986D50>}
> {'__ac_permissions__': (('View', ()),),
>  '__doc__': 'Mix-in class combining the most common set of basic
mix-ins\012
> ',
>  '__module__': 'OFS.SimpleItem',
>  'manage_options': ({'label': 'Security', 'action': 'manage_access'},)}
>
> ### Hmm, why does a propertysheet have no attributes or methods?
> >>> pprint(app.StuffMag.propertysheets.__dict__)
> {}
>
> ### Lets look a bit deeper again
> >>> pprint(app.StuffMag.propertysheets.__class__.__dict__)
> {'Properties': <ZInstanceSheet instance at 011FA320>,
>  '__doc__': 'StuffClass Property Sheets',
>  '__module__': None,
>  '__propset_attrs__': ('Properties',),
>  '_p_changed': None,
>  '_p_jar': <ZODB.Connection.Connection instance at 11ec6a0>,
>  '_p_oid': '\000\000\000\000\000\000\014P',
>  '_p_serial': '\0032\2441d!Pn',
>  'icon': ''}
>
> ### Properties looks like what we want
> >>> app.StuffMag.propertysheets.Properties
> <ZInstanceSheet instance at 011FA320>
>
> ### Lets take a look inside
> >>> pprint(app.StuffMag.propertysheets.Properties.__dict__)
> {'_md': {}, '_base': <ZCommonSheet instance at 012283F0>, 'id':
> 'Properties'}
>
> ### Hmm, have to look at the class again
> >>> pprint(app.StuffMag.propertysheets.Properties.__class__.__dict__)
> {'_Access_contents_information_Permission': '_View_Permission',
>  '_Manage_properties_Permission': '_Manage_properties_Permission',
>  '__ac_permissions__': (('Manage properties',
>                          ('manage_addProperty',
>                           'manage_editProperties',
>                           'manage_delProperties',
>                           'manage_changeProperties',
>                           'manage')),
>                         ('Access contents information',
>                          ('hasProperty',
>                           'propertyIds',
>                           'propertyValues',
>                           'propertyItems',
>                           ''))),
>  '__doc__': 'Waaa this is too hard',
>  '__module__': 'ZClasses.Property',
>  '__roles__': <PermissionRole instance at 009E8EF0>,
>  'hasProperty__roles__': <PermissionRole instance at 009E8EF0>,
>  'manage__roles__': <PermissionRole instance at 009E7820>,
>  'manage_addProperty__roles__': <PermissionRole instance at 009E7820>,
>  'manage_changeProperties__roles__': <PermissionRole instance at
009E7820>,
>  'manage_delProperties__roles__': <PermissionRole instance at 009E7820>,
>  'manage_editProperties__roles__': <PermissionRole instance at 009E7820>,
>  'propertyIds__roles__': <PermissionRole instance at 009E8EF0>,
>  'propertyItems__roles__': <PermissionRole instance at 009E8EF0>,
>  'propertyValues__roles__': <PermissionRole instance at 009E8EF0>,
>  'v_self': <function v_self at a021a0>}
>
> ### Hmm, lots of stuff, but I cant see what Im looking for, I'll guess
that
> acquisition plays a role here
> ### propertyItems under the permission 'Access contents information' looks
> awfully good
>
> >>> app.StuffMag.propertysheets.Properties.propertyItems
> <Python Method object at b50970>
>
>
> ### Wahooo - we have arrived,
> >>> app.StuffMag.propertysheets.Properties.propertyItems()
> [('heading', 'HI THERE!!!'), ('body', '')]
>
>
> Hmm, it only took an hour or so to figure this out.
>
>
> > -----Original Message-----
> > From: zope-dev-admin@zope.org [mailto:zope-dev-admin@zope.org]On Behalf
> > Of Michel Pelletier
> > Sent: Monday, February 28, 2000 2:24 PM
> > To: pam@digicool.com; zdp@zope.org
> > Cc: zope-dev@zope.org; support@digicool.com
> > Subject: [Zope-dev] The Debugger Is Your Friend
> >
> >
> >
> > And this document tells you why!  Last week James W. Howe expressed pain
> > in now knowing how to debug Zope, or even if it is possible.  Well, it
> > is, there is even a Zope debugger!  But, alas, until now it was
> > undocumented.
> >
> > So here it is folks, 'The Debugger Is Your Friend'.  This is a FIRST
> > DRAFT so please read it and send your comments and criticisms to me.
> >
> > The Debugger Is Your Friend
> >
> >   Jim Fulton is fond of the expression 'The debugger is your friend'.
> >   And although debugging Zope can sometimes be a bit painful, once you
> >   are used to the rythum the debugger truly is your friend, and will
> >   save you years off your life.
> >
> >   Zope has been designed to work in an integrated way with the Python
> >   debugger (pdb).  In order to really be able to use this document,
> >   you must know how to use pdb.  pdb is pretty simple as command line
> >   debuggers go, and anyone familiar with other popular command line
> >   debuggers (like gdb) will feel right at home in pdb.  If you are
> >   more familiar with the graphical, bells-and-whistles type debugger,
> >   I suggest you read the pdb documentation in the standard Python
> >   module documentation on the "Python website":http://www.python.org/.
> >
> >   For the purposes of this document, I will refer to the debugger as
> >   the 'Zope debugger', even though most of it is actually the Python
> >   debugger.  This is not to dis the python people, it's just a handy
> >   convention.  Whenever I refer to just the debugger that is not in
> >   the context of debugging Zope, I will say 'pdb'.
> >
> >   To debug Zope, you must first shut down the Zope process.  It is not
> >   possible to debug Zope and run it at the same time, as the debugger
> >   stops the entire process dead in its tracks (Note: If you are using
> >   ZEO, it is possible to run Zope and debug it simultaneously).  Also,
> >   starting up the debugger will by default start Zope in single
> >   threaded mode.  It is not possible to run the debugger and Zope in
> >   multi-threaded mode at the same time.  This is normally not an issue
> >   unless you are doing some pretty complex low level stuff, which you
> >   shouldn't be messing with anyway if you have to read this, right?
> >
> >   For most Zope developer's purposes, the debugger is needed to debug
> >   some sort of application level programming error.  A common scenario
> >   is when developing a new third party Product for Zope.  Products
> >   extend Zope's functionality but they also present the same kind of
> >   debugging problems that are commonly found in any programming
> >   environment.  It is useful to have an existing debugging
> >   infrastructure to help you jump immediatly to your new object and
> >   debug it and play with it directly in pdb.  The Zope debugger lets
> >   you do this.
> >
> >   It is also useful to actually debug Zope itself.  For example, if
> >   you discover some sort of obscure bug in Zope's object request
> >   broker (ZPublisher) it would be useful to tell Zope to stop at a
> >   certain point in ZPublisher and bring up the debugger.  The Zope
> >   debugger also lets you do this.
> >
> >   In reality, the 'Zope' part of the Zope debugger is actually just a
> >   handy way to start up Zope with some pre-configured break points and
> >   to tell the debugger where in Zope you want to start debugging.
> >   I'll stop talking now and give some examples.
> >
> >   Remember, for this example to work, you MUST shut down Zope.
> >   Debugging Zope starts in Zope's 'lib/python' directory.  'lib' is a
> >   sub-directory of Zope's top level directory.  Go to the 'lib/python'
> >   directory and fire up Python 1.5.2::
> >
> >     Python 1.5.2 (#0, Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)] on
> > win32
> >     Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
> >     >>> import Zope, ZPublisher
> >     >>>
> >
> >   Here we have run the python interpreter (which is where using the
> >   debugger takes place) and imported two modules, 'Zope' and
> >   'ZPublisher'.  If Python complains about an ImportError and not
> >   being able to find either module, then you are probably in the wrong
> >   directory, or you have not compiled Zope properly.
> >
> >   The Zope module is the main Zope application.  Now, in most cases,
> >   the term 'application' when used in Zope means some sort of
> >   application developed on top of Zope, but in this case, we mean Zope
> >   itself is the application.  The ZPublisher module is the Zope 'ORB'
> >   (object request broker) and is needed to run the debugger.
> >
> >   If trying to import these modules results in a 'cannot lock
> >   database' error, then another Zope process is using your database.
> >   Shut that Zope down if you want to debug it.  If you get error about
> >   'cannot open file such-and-such' then you do not have proper
> >   permission to read or write one of Zope's files.  Get the right
> >   permission and try again.
> >
> >   At this point, if python did not complain, then all is well and you
> >   are on your way to debugging Zope.  First, let's try something
> >   really neat.  Zope is, as you should know, completely protocol
> >   agnostic.  You do not need a webserver to actually call and use
> >   Zope, you can do it right here from this python prompt!  To
> >   illustrate this, let's call your Zope site and ask it for the very
> >   root level URL.  If your Zope website's URL is
> >   'http://www.mysite.org/', then the very root level URL is '' (or
> >   '/').  Calling this object with the debugger is as easy as, and just
> >   like *calling it through the web*.  The debugger will set up all
> >   kinds of fake environment to make Zope not know the difference
> >   between you calling it here with the debugger and accessing your top
> >   level root object::
> >
> >     >>> ZPublisher.Zope('')
> >     Status: 200 OK
> >     X-Powered-By: Zope (www.zope.org), Python (www.python.org)
> >     Content-Length: 1238
> >     Content-Type: text/html
> >
> >     <HTML><HEAD><TITLE>Zope</TITLE>
> >
> >       ... blah blah...
> >
> >     </BODY></HTML>
> >     >>>
> >
> >   Wow, wasn't that cool?  If you look closely, you will see that the
> >   content returned is *exactly* that which is returned when you call
> >   your root level object through HTTP, including all the HTTP headers.
> >
> >   So now let's suppose that you have an object in your Zope's root
> >   level folder called 'aFoo'.  aFoo is an instance of your Foo class
> >   You can call your aFoo instance with the debugger like so::
> >
> >     >>> ZPublisher.Zope('aFoo')
> >
> >   (or you could say '/aFoo' or '/aFoo/' etc..., the rules are the same
> >   as HTTP).  This is a handy way to test out your objects without ever
> >   leaving python.
> >
> >   Now, let's get to actually debugging your object.  Debugging,
> >   typically, involves running your 'program' up to a point where you
> >   think it's failing, and then inspecting the state of your variables
> >   and objects.  The easy part is the actual inspection, the hard part
> >   is getting your program to stop at the right point.  For example, if
> >   you suspect your logic is failing right at the one hundredth
> >   iteration in a large for loop, then you need to cleverly construct a
> >   breakpoint to stop your program right before this iteration.
> >
> >   The actual techniques of doing things like this, conditional
> >   breakpoints and other advanced bebugging, is beyond the scope of
> >   this document.  Many debugger come with documentation, but few come
> >   with an explanation of the 'Zen' of debugging.  Your best bet is to
> >   play with the debugger as much as possible, and to even use it to
> >   help you activly engineer your software.
> >
> >   So, for the sake or example, let's say that your 'aFoo' object is
> >   defined in a Zope Product called 'FooProduct', and is located in the
> >   'lib/python/Products/FooProduct' directory.  The class that defines
> >   the 'aFoo' instance is also called 'aFoo', and is defined in the
> >   'aFoo.py' module in your Product.  Therefore, from Zope's
> >   perspective, your aFoo's classes fully qualified name is
> >   'Products.FooProduct.aFoo.aFoo'.  All Zope objects have this
> >   kind of fully qualified name.  For example, the ZCatalog class can
> >   be found in 'Products.ZCatalog.ZCatalog.ZCatalog (The redundancy is
> >   because the Product, Module, and class are all named 'ZCatalog').
> >
> >   Now, your 'aFoo' class defines a method called 'fooMethod' that is
> >   definatly not doing the right thing for you.  Here is a possible
> >   definition of your class and method::
> >
> >     class aFoo:
> >
> >       def fooMethod(self, N):
> >         """ return the sum from 1 to N """
> >         sum = 0
> >         for x in range(N):
> >           sum = sum + x
> >         return sum
> >
> >
> >   This method can be called with N=5 through the web by visiting
> >   'http://www.mysite.org/aFoo/fooMethod?N=5'.  This will make Zope
> >   return the value '15', renderered into an HTML document.  There's
> >   actually nothing wrong with this method to debug, but for the sake
> >   of argument let's suppose that returning the sumation of N is not
> >   what we wanted, and therefore we want to debug this method.
> >
> >   Well then, let's fire up the debugger!  This is done in a very
> >   similar way to how we called Zope through the python interpreter
> >   before, except that we introduce one new argument to the call to
> >   'Zope'::
> >
> >     >>> ZPublisher.Zope('/aFoo/fooMethod?N=5', 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>(0)?()
> >     pdb>
> >
> >
> >   Here, we are calling Zope from the interpreter, just like before,
> >   but there are two differences.  First, we are specificaly calling
> >   our method ('fooMethod') with an argument, and second, we have
> >   provided a new argument to the Zope call, 'd=1'.  The 'd' argument,
> >   when true, causes Zope to fire up in the python debugger, pdb.
> >   Notice how the python prompt changed from '>>>' to 'pdb>'.  This
> >   indicates that we are in the debugger.
> >
> >   When you first fire up the debugger, Zope gives you a helpfull
> >   message that tells you how to get to your object.  To understand
> >   this message, it's useful to know how we have set Zope up to be
> >   debugged.  When Zope fires up in debugger mode, there are three
> >   breakpoints set for you automatically (if you don't know what a
> >   breakpoint is, you need to read the python debugger documentation).
> >   The first breakpoint stops the program at the point that ZPublisher
> >   (the Zope ORB) tries to publish the application module (in this
> >   case, the application module is 'Zope').  The second breakpoint
> >   stops the program right before ZPublisher tries to traverse down the
> >   provided URL path (in this case, '/aFoo/fooMethod').  The third
> >   breakpoint will stop the program right before ZPublisher calls the
> >   object it finds that matches the URL path (in this case, the 'aFoo'
> >   object).
> >
> >   So, the little blurb that comes up and tells you some keys to press
> >   is telling you these things in a terse way.  Hitting 's' will 's'tep
> >   you into the debugger, and hitting 'c' will 'c'ontinue the execution
> >   of the program until it hits a breakpoint.
> >
> >   Note however that none of these breakpoints will stop the program at
> >   'fooMethod'.  To stop the debugger right there, you need to tell the
> >   debugger to set a new breakpoint.  This is done quite easily.
> >   First, you must import your Python module (in this case, a Zope
> >   Product called 'aFoo', note the above discussion on 'fully
> >   qualified' names).
> >
> >     pdb> import Products
> >     pdb> b Products.FooProduct.aFoo.aFoo.fooMethod
> >     Breakpoint 5 at C:\Program
> > Files\WebSite\lib\python\Products\FooProduct\aFoo.py:42
> >     pdb>
> >
> >   First, we import 'Products'.  Since your module is a Zope Products,
> >   it can be found in the Products Module.  Next, we set a new
> >   breakpoint with the 'b'reak debugger command (pdb allows you to use
> >   single letter commands, but you could have also used the entire word
> >   'break').  The breakpoint we set is
> >   'Products.FooProduct.aFoo.aFoo.fooMethod'.  After setting this
> >   breakpoint, the debugger will respond that it found the method in
> >   question in a certain file, on a certain line (in this case, the
> >   ficticious line 42) and return you to the debugger.
> >
> >   Whew!  Lots of stuff to take in there in a few short paragraphs, but
> >   you are now on your way to debugging your method with style.  Now,
> >   we want to get to our 'fooMethod' so we can start debugging it.
> >   But along the way, we must first 'c'ontinue through the various
> >   breakpoints that Zope has set for us.  Although this may seem like a
> >   bit of a burden, it's actually quite good to get a feel for how Zope
> >   works internally by getting down the rythum that Zope uses to
> >   publish your object.  In these next examples, my inline comments
> >   will begin with '#".  Obviously, you won't see these comments when
> >   you are debugging.  So let's debug::
> >
> >     pdb> s
> >     # 's'tep into the actual debuging
> >
> >     > <string>(1)?()
> >     # this is pdb's response to being steped into, ignore it
> >
> >     pdb> c
> >     # now, let's 'c'ontinue onto the next breakpoint
> >
> >     > C:\Program
> > Files\WebSite\lib\python\ZPublisher\Publish.py(112)publish()
> >     -> def publish(request, module_name, after_list, debug=0,
> >
> >     # pdb has stoped at the first breakpoint, which is the point where
> >     # ZPubisher tries to publish the application module.
> >
> >     pdb> c
> >     # continuing onto the next breakpoint we get...
> >
> >     > C:\Program
> > Files\WebSite\lib\python\ZPublisher\Publish.py(101)call_object()
> >     -> def call_object(object, args, request):
> >
> >     # Here, ZPublisher (which is now publishing the application) has
> >     # found your object and is about to call it.  Let's jump out here
> >     # and discuss this point...
> >
> >     # 'Calling' your object consists of applying the arguments supplied
> > by
> >     # ZPublisher against the object.  Here, you can see how ZPublisher
> > is
> >     # passing three arguments into this process.  The first argument is
> >     # 'object' and is the actual object you want to call.  This can be
> >     # verified by 'p'rinting the object::
> >
> >     pdb> p object
> >     <aFoo instance at 00AFE410>
> >
> >     # Neat!  So here you can inspect your object (with the 'p'rint
> >     # command) and even play with it a bit, but we're not there yet!
> >     #
> >     # The next argument is 'args'.  This is a tuple of arguments that
> >     # ZPublisher will apply against your object call.
> >     #
> >     # The final argument is 'request'.  This is the 'request object' and
> >     # will eventually be transformed in to the DTML usable object
> > 'REQUEST'.
> >
> >     pdb> c
> >
> >     # Now let's continue, which breakpoint is next?  Yours!
> >
> >     > C:\Program
> > Files\WebSite\lib\python\Products\FooProduct\aFoo.py(42)fooMethod()
> >     -> def fooMethod(self, N)
> >
> >     # and now we are here, at YOUR method.  How can we be sure, well,
> >     # let's tell the debugger to show us where we are in the code:
> >
> >
> >     pdb> l
> >     41      def fooMethod(self, N):
> >     42        """ return the sum from 1 to N """
> >     43->      sum = 0
> >     44        for x in range(N):
> >     45          sum = sum + x
> >     46        return sum
> >
> >
> >   And that's it, really.  From here, with a little knowledge of the
> >   python debugger, you should be able to do any kind of debugging task
> >   that is needed.
> >
> > _______________________________________________
> > Zope-Dev maillist  -  Zope-Dev@zope.org
> > http://lists.zope.org/mailman/listinfo/zope-dev
> > **  No cross posts or HTML encoding!  **
> > (Related lists -
> >  http://lists.zope.org/mailman/listinfo/zope-announce
> >  http://lists.zope.org/mailman/listinfo/zope )
> >
>
>
> _______________________________________________
> Zope-Dev maillist  -  Zope-Dev@zope.org
> http://lists.zope.org/mailman/listinfo/zope-dev
> **  No cross posts or HTML encoding!  **
> (Related lists -
>  http://lists.zope.org/mailman/listinfo/zope-announce
>  http://lists.zope.org/mailman/listinfo/zope )
>