[tor-commits] [stem/master] Include the originating stacktrace in stem.descriptor.remote exceptions

atagar at torproject.org atagar at torproject.org
Tue Jan 1 00:29:39 UTC 2019


commit cc43a6ca90e3943a542258b2b4d8466ae9a4ac36
Author: Damian Johnson <atagar at torproject.org>
Date:   Sun Dec 30 12:19:10 2018 -0800

    Include the originating stacktrace in stem.descriptor.remote exceptions
    
    Our remote module needs to retain then later rethrow exceptions, which makes
    stacktraces less than helpful.
    
    Both python 2.x and 3.x have mechanisms for preserving stacktraces but they
    both rely on language syntax rather than libraries, so we cannot use either
    without breaking compatibility with the other version.
    
    As such opting for the least bad option I can think of which is to encode
    the original stacktrace within our message.
    
    As mentioned in the code's comment we'll opt for something better when we
    drop python 2.x support.
---
 stem/descriptor/remote.py | 35 ++++++++++++++++++++++++++++++++---
 1 file changed, 32 insertions(+), 3 deletions(-)

diff --git a/stem/descriptor/remote.py b/stem/descriptor/remote.py
index 13882ba7..8a2877fa 100644
--- a/stem/descriptor/remote.py
+++ b/stem/descriptor/remote.py
@@ -97,6 +97,7 @@ import random
 import sys
 import threading
 import time
+import traceback
 import zlib
 
 import stem
@@ -465,6 +466,13 @@ class Query(object):
     Blocks until our request is complete then provides the descriptors. If we
     haven't yet started our request then this does so.
 
+    .. versionchanged:: 1.8.0
+       Overwriting exceptions to include the originating traceback.
+
+       In Stem 2.x (when we no longer need Python 2.x compatibility) this will
+       revert back to re-raising the originating exception, but with its
+       stacktrace preserved.
+
     :param bool suppress: avoids raising exceptions if **True**
 
     :returns: list for the requested :class:`~stem.descriptor.__init__.Descriptor` instances
@@ -474,6 +482,7 @@ class Query(object):
       **False**...
 
         * **ValueError** if the descriptor contents is malformed
+        * **stem.ProtocolError** if unable to parse an ORPort response
         * **socket.timeout** if our request timed out
         * **urllib2.URLError** for most request failures
 
@@ -492,7 +501,27 @@ class Query(object):
         if suppress:
           return
 
-        raise self.error
+        # TODO: Unfortunately the proper way to retain a stacktrace differs
+        # between python 2.x and 3.x in a syntactic way...
+        #
+        #   Python 2.x
+        #
+        #     raise exc_type, exc_value, exc_traceback
+        #
+        #   Python 3.x
+        #
+        #     raise WrapperException('foo') from exc_value
+        #
+        # Because this is syntactic we cannot do an 'if python2, else python3'
+        # for this. As such re-encoding the stacktrace as part of the message.
+        #
+        # When we drop python 2.x support we should replace this with the
+        # 'raise from' option above.
+
+        exc_type, exc_value, exc_traceback = self.error
+        stacktrace = 'Original traceback:\n' + ''.join(traceback.format_exception(exc_type, exc_value, exc_traceback)[1:])
+
+        raise exc_type(str(exc_value) + '\n\n' + stacktrace)
       else:
         if self.content is None:
           if suppress:
@@ -523,7 +552,7 @@ class Query(object):
           for desc in results:
             yield desc
         except ValueError as exc:
-          self.error = exc  # encountered a parsing error
+          self.error = sys.exc_info()  # encountered a parsing error
 
           if suppress:
             return
@@ -577,7 +606,7 @@ class Query(object):
         return self._download_descriptors(retries - 1, timeout)
       else:
         log.debug("Unable to download descriptors from '%s': %s" % (self.download_url, exc))
-        self.error = exc
+        self.error = sys.exc_info()
     finally:
       self.is_done = True
 





More information about the tor-commits mailing list