[Patches] Patch: AttributeError and NameError: second attempt.

M.-A. Lemburg mal@lemburg.com
Fri, 26 May 2000 10:27:12 +0200


> Here's my latest attempt at improved error messages for NameError and
> AttributeError in Python.  This revision incorporates a suggestion
> independently made by Martin von Loewis and Mark Hammond: A NameError
> object holds the frame and name that caused it, and an AttributeError
> object holds the object and name that caused it.  For the main idea,
> see the changes to exceptions.py below.
> 
> The biggest advantage of this approach is that it lets us delay the
> process of generating an error message (which is O(n)) until we
> actually need to print it.  This should overcome the possible 
> technical objection Guido noted.
> 
> A secondary advantage (noted by Mr. von Loewis) is that putting the
> error-generation logic in Python makes it easier to improve our search
> for suggestions.  For example, a clever debugger (or a future version
> of Python) might give messags like:
>     * NameError: No such name as 'x'. Perhaps you meant 'self.x'?
>     * NameError: No such name as 'peice'. Perhaps you meant 'piece'?
> 
> A tertiary advantage, noted by Mr. Hammond, is that storing objects in
> AttributeError messages makes life easier for debugging and
> development tools.

The problem with storing objects in error objects is that they
can easily create (well) hidden circular references and cause
unwanted side-effects: 

1. A NameError that is being catched will
have a reference to the frame which currently executes (and this
references the variable holding the error object) -- the frame
will stay alive forever if the error object is not properly
cleaned up.

2. An AttributeError carrying around the object that caused
the error could cause finally-clauses to fail due to some
resource being kept alive or open.

I'd suggest not adding too much logic to the error objects
themselves, but rather to the code writing the tracebacks.

Since the above tricks are mainly intended to provide better
user feedback (which is implemented by writing a traceback),
this solves the problem without causing additional side-effects
or severe slow-downs. There are *very* many instances where
e.g. AttributeErrors are raised only to be catched and then
causing a different processing branch to be taken -- I would
strongly object if this action would slow down significantly
because I use this a lot ! BTW, getattr(...default) is not
an option, since these AttributeErrors are usually raised through
use of obj.attrname. The AttributeError then causes the lookup
to continue in some base object (aka Environmental Acquisition).

Here's an example of what I use to track down problems:

Traceback (innermost last):
  File "/data/home/lemburg/eGenix/ApplicationServer/Source/eGenix/Server/Request
    result = self.server.process_request(cgi)
  File "/data/home/lemburg/eGenix/ApplicationServer/Source/eGenix/Server/Request
    visitor.record_visit(cgi.pathinfo)
  File "/data/home/lemburg/eGenix/ApplicationServer/Source/eGenix/Server/Visitor
    del self.visits[: len(visits) - maxvisits + 1]
NameError: visits
### Dump of local variables:
#  .maxvisits       = 30
#  .now             = <built-in function now>
#    .__doc__         = 'now()\012\012Returns a DateTime-object reflecting the c
#    .__name__        = 'now'
#    .__self__        = None
#  .pathinfo        = '/demo/newsletter/query_newsletter.html'
#  .self            = <eGenix.Server.Visitor.Visitor instance at 831cea0>
#    .context         = <eGenix.Server.Visitor.VisitorContext (contact_query,ege
#    .id              = '/id0115895795388f019a584'
#    .last_visit      = <DateTime object for '2000-03-23 18:49:42.25' at 8260f80
#    .loginname       = 'root'
#    .preference      = <eGenix.Server.Visitor.VisitorPreference () at 0x833f830
#    .previous_visit  = <DateTime object for '2000-03-23 18:31:32.18' at 8317280
#    .visits          = [('/', <DateTime object for '2000-03-15 12:11:23.65' at 
#  .visit_time      = <DateTime object for '2000-03-23 18:49:42.25' at 8260f80>
#    .absdate         = 730202
#    .absdays         = 730201.784517
#    .abstime         = 67782.259286
...

-- 
Marc-Andre Lemburg
______________________________________________________________________
Business:                                      http://www.lemburg.com/
Python Pages:                           http://www.lemburg.com/python/