Imaplib2: use proper tracebacks for exceptions
Get real tracebacks (at the point that calls Request.abort() and not from the point that handles collected abort requests) and pass them to our calling functions to ease debugging of user problems. Signed-off-by: Eygene Ryabinkin <rea@codelabs.ru>
This commit is contained in:
parent
f69613965f
commit
7ab779ab21
@ -48,7 +48,7 @@ __author__ = "Piers Lauder <piers@janeelix.com>"
|
|||||||
__URL__ = "http://imaplib2.sourceforge.net"
|
__URL__ = "http://imaplib2.sourceforge.net"
|
||||||
__license__ = "Python License"
|
__license__ = "Python License"
|
||||||
|
|
||||||
import binascii, errno, os, Queue, random, re, select, socket, sys, time, threading, zlib
|
import binascii, errno, os, Queue, random, re, select, socket, sys, time, threading, traceback, zlib
|
||||||
|
|
||||||
select_module = select
|
select_module = select
|
||||||
|
|
||||||
@ -160,21 +160,25 @@ class Request(object):
|
|||||||
self.data = None
|
self.data = None
|
||||||
|
|
||||||
|
|
||||||
def abort(self, typ, val):
|
def abort_tb(self, typ, val, tb):
|
||||||
self.aborted = (typ, val)
|
self.aborted = (typ, val, tb)
|
||||||
self.deliver(None)
|
self.deliver(None)
|
||||||
|
|
||||||
|
|
||||||
|
def abort(self, typ, val):
|
||||||
|
self.abort_tb(typ, val, traceback.extract_stack())
|
||||||
|
|
||||||
|
|
||||||
def get_response(self, exc_fmt=None):
|
def get_response(self, exc_fmt=None):
|
||||||
self.callback = None
|
self.callback = None
|
||||||
if __debug__: self.parent._log(3, '%s:%s.ready.wait' % (self.name, self.tag))
|
if __debug__: self.parent._log(3, '%s:%s.ready.wait' % (self.name, self.tag))
|
||||||
self.ready.wait()
|
self.ready.wait()
|
||||||
|
|
||||||
if self.aborted is not None:
|
if self.aborted is not None:
|
||||||
typ, val = self.aborted
|
typ, val, tb = self.aborted
|
||||||
if exc_fmt is None:
|
if exc_fmt is None:
|
||||||
exc_fmt = '%s - %%s' % typ
|
exc_fmt = '%s - %%s' % typ
|
||||||
raise typ(exc_fmt % str(val))
|
raise typ, typ(exc_fmt % str(val)), tb
|
||||||
|
|
||||||
return self.response
|
return self.response
|
||||||
|
|
||||||
@ -1661,7 +1665,7 @@ class IMAP4(object):
|
|||||||
|
|
||||||
if __debug__: self._log(1, 'starting')
|
if __debug__: self._log(1, 'starting')
|
||||||
|
|
||||||
typ, val = self.abort, 'connection terminated'
|
typ, val, tb = self.abort, 'connection terminated', None
|
||||||
|
|
||||||
while not self.Terminate:
|
while not self.Terminate:
|
||||||
|
|
||||||
@ -1684,6 +1688,7 @@ class IMAP4(object):
|
|||||||
if resp_timeout is not None and self.tagged_commands:
|
if resp_timeout is not None and self.tagged_commands:
|
||||||
if __debug__: self._log(1, 'response timeout')
|
if __debug__: self._log(1, 'response timeout')
|
||||||
typ, val = self.abort, 'no response after %s secs' % resp_timeout
|
typ, val = self.abort, 'no response after %s secs' % resp_timeout
|
||||||
|
tb = traceback.extract_stack()
|
||||||
break
|
break
|
||||||
continue
|
continue
|
||||||
if self.idle_timeout > time.time():
|
if self.idle_timeout > time.time():
|
||||||
@ -1695,23 +1700,33 @@ class IMAP4(object):
|
|||||||
if __debug__: self._log(1, 'inq None - terminating')
|
if __debug__: self._log(1, 'inq None - terminating')
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# self.inq can contain tuple, it means we got an exception.
|
||||||
|
# For example of code that produces tuple see IMAP4._reader()
|
||||||
|
# and IMAP4._writer().
|
||||||
|
#
|
||||||
|
# XXX: may be it will be more explicit to create own exception
|
||||||
|
# XXX: class and pass it instead of tuple.
|
||||||
if not isinstance(line, basestring):
|
if not isinstance(line, basestring):
|
||||||
typ, val = line
|
typ, val, tb = line
|
||||||
break
|
break
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._put_response(line)
|
self._put_response(line)
|
||||||
except:
|
except:
|
||||||
typ, val = self.error, 'program error: %s - %s' % sys.exc_info()[:2]
|
typ, val = self.error, 'program error: %s - %s' % sys.exc_info()[:2]
|
||||||
|
tb = sys.exc_info()[2]
|
||||||
break
|
break
|
||||||
|
|
||||||
self.Terminate = True
|
self.Terminate = True
|
||||||
|
|
||||||
|
if not tb:
|
||||||
|
tb = traceback.extract_stack()
|
||||||
|
|
||||||
if __debug__: self._log(1, 'terminating: %s' % repr(val))
|
if __debug__: self._log(1, 'terminating: %s' % repr(val))
|
||||||
|
|
||||||
while not self.ouq.empty():
|
while not self.ouq.empty():
|
||||||
try:
|
try:
|
||||||
self.ouq.get_nowait().abort(typ, val)
|
self.ouq.get_nowait().abort_tb(typ, val, tb)
|
||||||
except Queue.Empty:
|
except Queue.Empty:
|
||||||
break
|
break
|
||||||
self.ouq.put(None)
|
self.ouq.put(None)
|
||||||
@ -1719,7 +1734,7 @@ class IMAP4(object):
|
|||||||
self.commands_lock.acquire()
|
self.commands_lock.acquire()
|
||||||
for name in self.tagged_commands.keys():
|
for name in self.tagged_commands.keys():
|
||||||
rqb = self.tagged_commands.pop(name)
|
rqb = self.tagged_commands.pop(name)
|
||||||
rqb.abort(typ, val)
|
rqb.abort_tb(typ, val, tb)
|
||||||
self.state_change_free.set()
|
self.state_change_free.set()
|
||||||
self.commands_lock.release()
|
self.commands_lock.release()
|
||||||
if __debug__: self._log(3, 'state_change_free.set')
|
if __debug__: self._log(3, 'state_change_free.set')
|
||||||
@ -1801,7 +1816,7 @@ class IMAP4(object):
|
|||||||
self._print_log()
|
self._print_log()
|
||||||
if self.debug: self.debug += 4 # Output all
|
if self.debug: self.debug += 4 # Output all
|
||||||
self._log(1, reason)
|
self._log(1, reason)
|
||||||
self.inq.put((self.abort, reason))
|
self.inq.put((self.abort, reason, sys.exc_info()[2]))
|
||||||
break
|
break
|
||||||
|
|
||||||
poll.unregister(self.read_fd)
|
poll.unregister(self.read_fd)
|
||||||
@ -1865,7 +1880,7 @@ class IMAP4(object):
|
|||||||
self._print_log()
|
self._print_log()
|
||||||
if self.debug: self.debug += 4 # Output all
|
if self.debug: self.debug += 4 # Output all
|
||||||
self._log(1, reason)
|
self._log(1, reason)
|
||||||
self.inq.put((self.abort, reason))
|
self.inq.put((self.abort, reason, sys.exc_info()[2]))
|
||||||
break
|
break
|
||||||
|
|
||||||
if __debug__: self._log(1, 'finished')
|
if __debug__: self._log(1, 'finished')
|
||||||
@ -1878,6 +1893,7 @@ class IMAP4(object):
|
|||||||
if __debug__: self._log(1, 'starting')
|
if __debug__: self._log(1, 'starting')
|
||||||
|
|
||||||
reason = 'Terminated'
|
reason = 'Terminated'
|
||||||
|
tb = None
|
||||||
|
|
||||||
while not self.Terminate:
|
while not self.Terminate:
|
||||||
rqb = self.ouq.get()
|
rqb = self.ouq.get()
|
||||||
@ -1889,15 +1905,19 @@ class IMAP4(object):
|
|||||||
if __debug__: self._log(4, '> %s' % rqb.data)
|
if __debug__: self._log(4, '> %s' % rqb.data)
|
||||||
except:
|
except:
|
||||||
reason = 'socket error: %s - %s' % sys.exc_info()[:2]
|
reason = 'socket error: %s - %s' % sys.exc_info()[:2]
|
||||||
|
tb = sys.exc_info()[2]
|
||||||
if __debug__:
|
if __debug__:
|
||||||
if not self.Terminate:
|
if not self.Terminate:
|
||||||
self._print_log()
|
self._print_log()
|
||||||
if self.debug: self.debug += 4 # Output all
|
if self.debug: self.debug += 4 # Output all
|
||||||
self._log(1, reason)
|
self._log(1, reason)
|
||||||
rqb.abort(self.abort, reason)
|
rqb.abort_tb(self.abort, reason, tb)
|
||||||
break
|
break
|
||||||
|
|
||||||
self.inq.put((self.abort, reason))
|
if not tb:
|
||||||
|
tb = traceback.extract_stack()
|
||||||
|
|
||||||
|
self.inq.put((self.abort, reason, tb))
|
||||||
|
|
||||||
if __debug__: self._log(1, 'finished')
|
if __debug__: self._log(1, 'finished')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user