[Python-ideas] Verbose traceback formatting

Oleg Broytman phd at phdru.name
Wed Aug 29 02:05:38 CEST 2012


Hi!

On Tue, Aug 28, 2012 at 06:26:04PM -0400, Mike Graham <mikegraham at gmail.com> wrote:
> It's possible to give a lot more on error than the default traceback
> gives you. I propose that Python should ship a more verbose formatter

   Good idea!

> and a command line switch to use it.

   And an environment variable, as usual: PYTHONTRACEBACK=verbose.

> Here's an example of IPython's verbose formatter. I wrote a buggy program:
> 
> > def f(a):
> >     x = a * 4
> >     y = a - 4
> >     return x / y
> >
> > def main():
> >     for i in xrange(100):
> >         f(i)
> >
> > main()
> 
> 
> and then ran it in IPython with verbose tracebacks and got the following output:
> 
> > ZeroDivisionError                         Traceback (most recent call last)
> >
> > /home/mike/foo.py in <module>()
> >       8         f(i)
> >       9
> > ---> 10 main()
> >         global main = <function main at 0x10bd7d0>
> >      11
> >      12
> >
> > /home/mike/foo.py in main()
> >       6 def main():
> >       7     for i in xrange(100):
> > ----> 8         f(i)
> >         global f = <function f at 0x10bd758>
> >         i = 4
> >       9
> >      10 main()
> >
> > /home/mike/foo.py in f(a=4)
> >       2     x = a * 4
> >       3     y = a - 4
> > ----> 4     return x / y
> >         x = 16
> >         y = 0
> >       5
> >       6 def main():
> >
> > ZeroDivisionError: integer division or modulo by zero
> 
> 
> 
> This is very handy!

   100% agree! py.test produces even more verbose tracebacks and I found
them very helpful in debugging. Here is a short example and below is
much bigger one.

___________________________ test_transaction_delete ____________________________

close = False

    def test_transaction_delete(close=False):
        if not supports('transactions'):
            return
        setupClass(TestSOTrans)
        trans = TestSOTrans._connection.transaction()
        try:
            TestSOTrans(name='bob')
            bIn = TestSOTrans.byName('bob', connection=trans)
            bIn.destroySelf()
            bOut = TestSOTrans.select(TestSOTrans.q.name=='bob')
>           assert bOut.count() == 1
E           assert 0 == 1
E            +  where 0 = <SelectResults at a6eb7cc>.count()

test_transactions.py:65: AssertionError

   Longer and more verbose:

_______________________________ test_transaction _______________________________

    def test_transaction():
        if not supports('transactions'):
            return
>       setupClass(TestSOTrans)

test_transactions.py:17: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

soClasses = [<class 'sqlobject.tests.test_transactions.TestSOTrans'>]
force = False

    def setupClass(soClasses, force=False):
        """
        Makes sure the classes have a corresponding and correct table.
        This won't recreate the table if it already exists.  It will check
        that the table is properly defined (in case you change your table
        definition).
    
        You can provide a single class or a list of classes; if a list
        then classes will be created in the order you provide, and
        destroyed in the opposite order.  So if class A depends on class
        B, then do setupClass([B, A]) and B won't be destroyed or cleared
        until after A is destroyed or cleared.
    
        If force is true, then the database will be recreated no matter
        what.
        """
        global hub
        if not isinstance(soClasses, (list, tuple)):
            soClasses = [soClasses]
        connection = getConnection()
        for soClass in soClasses:
            ## This would be an alternate way to register connections...
            #try:
            #    hub
            #except NameError:
            #    hub = sqlobject.dbconnection.ConnectionHub()
            #soClass._connection = hub
            #hub.threadConnection = connection
            #hub.processConnection = connection
            soClass._connection = connection
>       installOrClear(soClasses, force=force)

dbtest.py:83: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

cls = <class 'sqlobject.tests.dbtest.InstalledTestDatabase'>
soClasses = [<class 'sqlobject.tests.test_transactions.TestSOTrans'>]
force = False

    @classmethod
    def installOrClear(cls, soClasses, force=False):
        cls.setup()
        reversed = list(soClasses)[:]
        reversed.reverse()
        # If anything needs to be dropped, they all must be dropped
        # But if we're forcing it, then we'll always drop
        if force:
            any_drops = True
        else:
            any_drops = False
        for soClass in reversed:
            table = soClass.sqlmeta.table
>           if not soClass._connection.tableExists(table):

dbtest.py:140: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlobject.postgres.pgconnection.PostgresConnection instance at 0x8c2fa2c>
tableName = 'test_so_trans'

    def tableExists(self, tableName):
        result = self.queryOne("SELECT COUNT(relname) FROM pg_class WHERE relname = %s"
>                              % self.sqlrepr(tableName))

../postgres/pgconnection.py:235: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlobject.postgres.pgconnection.PostgresConnection instance at 0x8c2fa2c>
s = "SELECT COUNT(relname) FROM pg_class WHERE relname = 'test_so_trans'"

    def queryOne(self, s):
>       return self._runWithConnection(self._queryOne, s)

../dbconnection.py:457: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlobject.postgres.pgconnection.PostgresConnection instance at 0x8c2fa2c>
meth = <bound method PostgresConnection._queryOne of <sqlobject.postgres.pgconnection.PostgresConnection instance at 0x8c2fa2c>>

    def _runWithConnection(self, meth, *args):
>       conn = self.getConnection()

../dbconnection.py:325: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlobject.postgres.pgconnection.PostgresConnection instance at 0x8c2fa2c>

    def getConnection(self):
        self._poolLock.acquire()
        try:
            if not self._pool:
>               conn = self.makeConnection()

../dbconnection.py:336: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <sqlobject.postgres.pgconnection.PostgresConnection instance at 0x8c2fa2c>

    def makeConnection(self):
        try:
            if self.use_dsn:
                conn = self.module.connect(self.dsn)
            else:
                conn = self.module.connect(**self.dsn_dict)
        except self.module.OperationalError, e:
>           raise OperationalError("%s; used connection string %r" % (e, self.dsn))
E           OperationalError: could not connect to server: No such file or directory
E           	Is the server running locally and accepting
E           	connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
E           ; used connection string 'dbname=test'

../postgres/pgconnection.py:142: OperationalError

Oleg.
-- 
     Oleg Broytman            http://phdru.name/            phd at phdru.name
           Programmers don't die, they just GOSUB without RETURN.



More information about the Python-ideas mailing list