commit 0e32da680122a669958adb8f18b660e5d3e3c498 Author: Damian Johnson atagar@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)
tor-commits@lists.torproject.org