[tor-commits] [stem/master] Move DownloadFailed into base module

atagar at torproject.org atagar at torproject.org
Sat Aug 17 20:44:27 UTC 2019


commit 0e32da680122a669958adb8f18b660e5d3e3c498
Author: Damian Johnson <atagar at torproject.org>
Date:   Fri Aug 2 12:46:47 2019 -0700

    Move DownloadFailed into base module
    
    While I love colocating this with the download() helper, when it comes to
    practical usage keeping module names of common exceptions like this short
    are important for usability.
---
 stem/__init__.py              | 66 ++++++++++++++++++++++++++++++++++++++++
 stem/descriptor/collector.py  | 10 +++----
 stem/util/connection.py       | 70 +++----------------------------------------
 test/integ/util/connection.py |  5 ++--
 test/unit/util/connection.py  |  5 ++--
 5 files changed, 81 insertions(+), 75 deletions(-)

diff --git a/stem/__init__.py b/stem/__init__.py
index 70870e8e..dc8ae2d2 100644
--- a/stem/__init__.py
+++ b/stem/__init__.py
@@ -28,6 +28,9 @@ Library for working with the tor process.
     +- SocketError - Communication with the socket failed.
        +- SocketClosed - Socket has been shut down.
 
+  DownloadFailed - Inability to download a resource.
+    +- DownloadTimeout - Download timeout reached.
+
 .. data:: Runlevel (enum)
 
   Rating of importance used for event logging.
@@ -499,6 +502,8 @@ Library for working with the tor process.
   ================= ===========
 """
 
+import traceback
+
 import stem.util
 import stem.util.enum
 
@@ -532,6 +537,8 @@ __all__ = [
   'InvalidArguments',
   'SocketError',
   'SocketClosed',
+  'DownloadFailed',
+  'DownloadTimeout',
   'Runlevel',
   'Signal',
   'Flag',
@@ -712,6 +719,65 @@ class SocketClosed(SocketError):
   'Control socket was closed before completing the message.'
 
 
+class DownloadFailed(IOError):
+  """
+  Inability to download a resource. Python's urllib module raises
+  a wide variety of undocumented exceptions (urllib2.URLError,
+  socket.timeout, and others).
+
+  This wraps lower level failures in a common exception type that
+  retains their exception and `stacktrace
+  <https://docs.python.org/3/library/traceback.html>`_.
+
+  .. versionadded:: 1.8.0
+
+  :var str url: url we failed to download from
+  :var Exception error: original urllib exception
+  :var traceback stacktrace: original stacktrace
+  :var str stacktrace_str: string representation of the stacktrace
+  """
+
+  def __init__(self, url, error, stacktrace, message = None):
+    if message is None:
+      # The string representation of exceptions can reside in several places.
+      # urllib.URLError use a 'reason' attribute that in turn may referrence
+      # low level structures such as socket.gaierror. Whereas most exceptions
+      # use a 'message' attribute.
+
+      reason = str(error)
+
+      all_str_repr = (
+        getattr(getattr(error, 'reason', None), 'strerror', None),
+        getattr(error, 'reason', None),
+        getattr(error, 'message', None),
+      )
+
+      for str_repr in all_str_repr:
+        if str_repr and isinstance(str_repr, str):
+          reason = str_repr
+          break
+
+      message = 'Failed to download from %s (%s): %s' % (url, type(error).__name__, reason)
+
+    super(DownloadFailed, self).__init__(message)
+
+    self.url = url
+    self.error = error
+    self.stacktrace = stacktrace
+    self.stacktrace_str = ''.join(traceback.format_tb(stacktrace))
+
+
+class DownloadTimeout(DownloadFailed):
+  """
+  Timeout reached while downloading this resource.
+
+  .. versionadded:: 1.8.0
+  """
+
+  def __init__(self, url, error, stacktrace, timeout):
+    super(DownloadTimeout, self).__init__('Failed to download from %s: %0.1f second timeout reached' % (url, timeout))
+
+
 Runlevel = stem.util.enum.UppercaseEnum(
   'DEBUG',
   'INFO',
diff --git a/stem/descriptor/collector.py b/stem/descriptor/collector.py
index d9f159e1..57f9ce0a 100644
--- a/stem/descriptor/collector.py
+++ b/stem/descriptor/collector.py
@@ -141,7 +141,7 @@ def get_server_descriptors(start = None, end = None, cache_to = None, timeout =
     :class:`~stem.descriptor.server_descriptor.ServerDescriptor` for the given
     time range
 
-  :raises: :class:`~stem.util.connection.DownloadFailed` if the download fails
+  :raises: :class:`~stem.DownloadFailed` if the download fails
   """
 
   for f in get_instance().files('server-descriptor', start, end):
@@ -209,7 +209,7 @@ class File(object):
     :raises:
       * **ValueError** if unable to determine the descirptor type
       * **TypeError** if we cannot parse this descriptor type
-      * :class:`~stem.util.connection.DownloadFailed` if the download fails
+      * :class:`~stem.DownloadFailed` if the download fails
     """
 
     if descriptor_type is None:
@@ -256,7 +256,7 @@ class File(object):
 
     :returns: **str** with the path we downloaded to
 
-    :raises: :class:`~stem.util.connection.DownloadFailed` if the download fails
+    :raises: :class:`~stem.DownloadFailed` if the download fails
     """
 
     # TODO: If checksums get added to the index we should replace
@@ -383,7 +383,7 @@ class CollecTor(object):
 
         * **ValueError** if json is malformed
         * **IOError** if unable to decompress
-        * :class:`~stem.util.connection.DownloadFailed` if the download fails
+        * :class:`~stem.DownloadFailed` if the download fails
     """
 
     if not self._cached_index or time.time() - self._cached_index_at >= REFRESH_INDEX_RATE:
@@ -419,7 +419,7 @@ class CollecTor(object):
 
         * **ValueError** if json is malformed
         * **IOError** if unable to decompress
-        * :class:`~stem.util.connection.DownloadFailed` if the download fails
+        * :class:`~stem.DownloadFailed` if the download fails
     """
 
     if not self._cached_files or time.time() - self._cached_index_at >= REFRESH_INDEX_RATE:
diff --git a/stem/util/connection.py b/stem/util/connection.py
index 7be7fe09..a1f93625 100644
--- a/stem/util/connection.py
+++ b/stem/util/connection.py
@@ -8,9 +8,6 @@ Connection and networking based utility functions.
 
 ::
 
-  DownloadFailed - Inability to download a resource.
-    +- DownloadTimeout - Download timeout reached.
-
   download - download from a given url
   get_connections - quieries the connections belonging to a given process
   system_resolvers - provides connection resolution methods that are likely to be available
@@ -65,8 +62,8 @@ import re
 import socket
 import sys
 import time
-import traceback
 
+import stem
 import stem.util
 import stem.util.proc
 import stem.util.system
@@ -176,65 +173,6 @@ class Connection(collections.namedtuple('Connection', ['local_address', 'local_p
   """
 
 
-class DownloadFailed(IOError):
-  """
-  Inability to download a resource. Python's urllib module raises
-  a wide variety of undocumented exceptions (urllib2.URLError,
-  socket.timeout, and others).
-
-  This wraps lower level failures in a common exception type that
-  retains their exception and `stacktrace
-  <https://docs.python.org/3/library/traceback.html>`_.
-
-  .. versionadded:: 1.8.0
-
-  :var str url: url we failed to download from
-  :var Exception error: original urllib exception
-  :var traceback stacktrace: original stacktrace
-  :var str stacktrace_str: string representation of the stacktrace
-  """
-
-  def __init__(self, url, error, stacktrace, message = None):
-    if message is None:
-      # The string representation of exceptions can reside in several places.
-      # urllib.URLError use a 'reason' attribute that in turn may referrence
-      # low level structures such as socket.gaierror. Whereas most exceptions
-      # use a 'message' attribute.
-
-      reason = str(error)
-
-      all_str_repr = (
-        getattr(getattr(error, 'reason', None), 'strerror', None),
-        getattr(error, 'reason', None),
-        getattr(error, 'message', None),
-      )
-
-      for str_repr in all_str_repr:
-        if str_repr and isinstance(str_repr, str):
-          reason = str_repr
-          break
-
-      message = 'Failed to download from %s (%s): %s' % (url, type(error).__name__, reason)
-
-    super(DownloadFailed, self).__init__(message)
-
-    self.url = url
-    self.error = error
-    self.stacktrace = stacktrace
-    self.stacktrace_str = ''.join(traceback.format_tb(stacktrace))
-
-
-class DownloadTimeout(DownloadFailed):
-  """
-  Timeout reached while downloading this resource.
-
-  .. versionadded:: 1.8.0
-  """
-
-  def __init__(self, url, error, stacktrace, timeout):
-    super(DownloadTimeout, self).__init__('Failed to download from %s: %0.1f second timeout reached' % (url, timeout))
-
-
 def download(url, timeout = None, retries = None):
   """
   Download from the given url.
@@ -248,7 +186,7 @@ def download(url, timeout = None, retries = None):
 
   :returns: **bytes** content of the given url
 
-  :raises: :class:`~stem.util.connection.DownloadFailed` if the download fails
+  :raises: :class:`~stem.DownloadFailed` if the download fails
   """
 
   if retries is None:
@@ -259,7 +197,7 @@ def download(url, timeout = None, retries = None):
   try:
     return urllib.urlopen(url, timeout = timeout).read()
   except socket.timeout as exc:
-    raise DownloadTimeout(url, exc, sys.exc_info()[2], timeout)
+    raise stem.DownloadTimeout(url, exc, sys.exc_info()[2], timeout)
   except:
     exc, stacktrace = sys.exc_info()[1:3]
 
@@ -271,7 +209,7 @@ def download(url, timeout = None, retries = None):
       return download(url, timeout, retries - 1)
     else:
       log.debug('Failed to download from %s: %s' % (url, exc))
-      raise DownloadFailed(url, exc, stacktrace)
+      raise stem.DownloadFailed(url, exc, stacktrace)
 
 
 def get_connections(resolver = None, process_pid = None, process_name = None):
diff --git a/test/integ/util/connection.py b/test/integ/util/connection.py
index 4617fe56..ed55ad89 100644
--- a/test/integ/util/connection.py
+++ b/test/integ/util/connection.py
@@ -5,6 +5,7 @@ that we're running.
 
 import unittest
 
+import stem
 import stem.util.connection
 import stem.util.system
 import test.require
@@ -54,8 +55,8 @@ class TestConnection(unittest.TestCase):
   def test_download_failure(self):
     try:
       stem.util.connection.download('https://no.such.testing.url')
-      self.fail('expected a stem.util.connection.DownloadFailed to be raised')
-    except stem.util.connection.DownloadFailed as exc:
+      self.fail('expected a stem.DownloadFailed to be raised')
+    except stem.DownloadFailed as exc:
       self.assertEqual('Failed to download from https://no.such.testing.url (URLError): Name or service not known', str(exc))
       self.assertEqual('https://no.such.testing.url', exc.url)
       self.assertEqual('Name or service not known', exc.error.reason.strerror)
diff --git a/test/unit/util/connection.py b/test/unit/util/connection.py
index 57718446..8721ff6d 100644
--- a/test/unit/util/connection.py
+++ b/test/unit/util/connection.py
@@ -6,6 +6,7 @@ import io
 import platform
 import unittest
 
+import stem
 import stem.util.connection
 
 from stem.util.connection import Resolver, Connection
@@ -189,8 +190,8 @@ class TestConnection(unittest.TestCase):
 
     try:
       stem.util.connection.download(URL)
-      self.fail('expected a stem.util.connection.DownloadFailed to be raised')
-    except stem.util.connection.DownloadFailed as exc:
+      self.fail('expected a stem.DownloadFailed to be raised')
+    except stem.DownloadFailed as exc:
       self.assertEqual('Failed to download from https://example.unit.test.url (URLError): boom', str(exc))
       self.assertEqual(URL, exc.url)
       self.assertEqual('boom', exc.error.reason)





More information about the tor-commits mailing list