[tor-commits] [stem/master] Conforming to E302 (two blank lines above methods and functions)
atagar at torproject.org
atagar at torproject.org
Mon Jan 7 09:08:00 UTC 2013
commit 26b27b6eff21214b17b4018a16ac97f3bd674734
Author: Damian Johnson <atagar at torproject.org>
Date: Mon Jan 7 00:31:22 2013 -0800
Conforming to E302 (two blank lines above methods and functions)
Another long one. I hope this is almost done...
---
run_tests.py | 2 +
stem/__init__.py | 9 +++++
stem/connection.py | 34 ++++++++++++++++++++
stem/control.py | 5 +++
stem/descriptor/__init__.py | 6 +++
stem/descriptor/export.py | 3 ++
stem/descriptor/extrainfo_descriptor.py | 5 +++
stem/descriptor/networkstatus.py | 12 +++++++
stem/descriptor/reader.py | 9 +++++
stem/descriptor/router_status_entry.py | 13 +++++++
stem/descriptor/server_descriptor.py | 4 ++
stem/exit_policy.py | 6 +++
stem/prereq.py | 5 +++
stem/process.py | 3 ++
stem/response/__init__.py | 6 +++
stem/response/authchallenge.py | 1 +
stem/response/events.py | 20 +++++++++++
stem/response/getconf.py | 1 +
stem/response/getinfo.py | 1 +
stem/response/mapaddress.py | 1 +
stem/response/protocolinfo.py | 1 +
stem/socket.py | 6 +++
stem/util/conf.py | 6 +++
stem/util/connection.py | 11 ++++++
stem/util/enum.py | 2 +
stem/util/log.py | 15 +++++++++
stem/util/ordereddict.py | 1 +
stem/util/proc.py | 14 ++++++++
stem/util/str_tools.py | 8 +++++
stem/util/system.py | 12 +++++++
stem/util/term.py | 1 +
stem/util/tor_tools.py | 5 +++
stem/version.py | 3 ++
test/check_whitespace.py | 5 ++-
test/integ/connection/authentication.py | 3 ++
test/integ/connection/connect.py | 1 +
test/integ/control/base_controller.py | 2 +
test/integ/control/controller.py | 1 +
test/integ/descriptor/__init__.py | 1 +
test/integ/descriptor/extrainfo_descriptor.py | 1 +
test/integ/descriptor/networkstatus.py | 1 +
test/integ/descriptor/reader.py | 5 +++
test/integ/descriptor/server_descriptor.py | 1 +
test/integ/process.py | 2 +
test/integ/response/protocolinfo.py | 1 +
test/integ/socket/control_message.py | 1 +
test/integ/socket/control_socket.py | 1 +
test/integ/util/conf.py | 3 ++
test/integ/util/proc.py | 1 +
test/integ/util/system.py | 3 ++
test/integ/version.py | 1 +
test/mocking.py | 29 +++++++++++++++++
test/network.py | 4 ++
test/output.py | 10 ++++++
test/prompt.py | 5 +++
test/runner.py | 11 ++++++
test/unit/connection/authentication.py | 1 +
test/unit/control/controller.py | 1 +
test/unit/descriptor/export.py | 1 +
test/unit/descriptor/extrainfo_descriptor.py | 1 +
.../networkstatus/directory_authority.py | 1 +
test/unit/descriptor/networkstatus/document_v2.py | 1 +
test/unit/descriptor/networkstatus/document_v3.py | 1 +
.../descriptor/networkstatus/key_certificate.py | 1 +
test/unit/descriptor/reader.py | 1 +
test/unit/descriptor/router_status_entry.py | 1 +
test/unit/descriptor/server_descriptor.py | 1 +
test/unit/exit_policy/policy.py | 1 +
test/unit/exit_policy/rule.py | 1 +
test/unit/response/authchallenge.py | 1 +
test/unit/response/control_line.py | 1 +
test/unit/response/control_message.py | 1 +
test/unit/response/events.py | 1 +
test/unit/response/getconf.py | 1 +
test/unit/response/getinfo.py | 1 +
test/unit/response/mapaddress.py | 1 +
test/unit/response/protocolinfo.py | 1 +
test/unit/response/singleline.py | 1 +
test/unit/tutorial.py | 1 +
test/unit/util/conf.py | 1 +
test/unit/util/connection.py | 1 +
test/unit/util/enum.py | 1 +
test/unit/util/proc.py | 1 +
test/unit/util/str_tools.py | 1 +
test/unit/util/system.py | 2 +
test/unit/util/tor_tools.py | 1 +
test/unit/version.py | 1 +
test/util.py | 2 +
88 files changed, 357 insertions(+), 1 deletions(-)
diff --git a/run_tests.py b/run_tests.py
index f9b18fc..9774c55 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -165,6 +165,7 @@ INTEG_TESTS = (
test.integ.control.controller.TestController,
)
+
def load_user_configuration(test_config):
"""
Parses our commandline arguments, loading our custom test configuration if
@@ -254,6 +255,7 @@ def load_user_configuration(test_config):
print " TRACE, DEBUG, INFO, NOTICE, WARN, ERROR"
sys.exit(1)
+
def _clean_orphaned_pyc():
test.output.print_noline(" checking for orphaned .pyc files... ", *test.runner.STATUS_ATTR)
diff --git a/stem/__init__.py b/stem/__init__.py
index f02897c..45bf14d 100644
--- a/stem/__init__.py
+++ b/stem/__init__.py
@@ -403,12 +403,15 @@ import stem.util.enum
UNDEFINED = "<Undefined_ >"
+
class ControllerError(Exception):
"Base error for controller communication issues."
+
class ProtocolError(ControllerError):
"Malformed content from the control socket."
+
class OperationFailed(ControllerError):
"""
Base exception class for failed operations that return an error code
@@ -423,11 +426,13 @@ class OperationFailed(ControllerError):
self.code = code
self.message = message
+
class UnsatisfiableRequest(OperationFailed):
"""
Exception raised if Tor was unable to process our request.
"""
+
class CircuitExtensionFailed(UnsatisfiableRequest):
"""
An attempt to create or extend a circuit failed.
@@ -439,11 +444,13 @@ class CircuitExtensionFailed(UnsatisfiableRequest):
super(CircuitExtensionFailed, self).__init__(message = message)
self.circ = circ
+
class InvalidRequest(OperationFailed):
"""
Exception raised when the request was invalid or malformed.
"""
+
class InvalidArguments(InvalidRequest):
"""
Exception class for requests which had invalid arguments.
@@ -458,9 +465,11 @@ class InvalidArguments(InvalidRequest):
super(InvalidArguments, self).__init__(code, message)
self.arguments = arguments
+
class SocketError(ControllerError):
"Error arose while communicating with the control socket."
+
class SocketClosed(SocketError):
"Control socket was closed before completing the message."
diff --git a/stem/connection.py b/stem/connection.py
index 0291a82..ef7ba04 100644
--- a/stem/connection.py
+++ b/stem/connection.py
@@ -120,6 +120,7 @@ AuthMethod = stem.util.enum.Enum("NONE", "PASSWORD", "COOKIE", "SAFECOOKIE", "UN
CLIENT_HASH_CONSTANT = "Tor safe cookie authentication controller-to-server hash"
SERVER_HASH_CONSTANT = "Tor safe cookie authentication server-to-controller hash"
+
def connect_port(control_addr = "127.0.0.1", control_port = 9051, password = None, chroot_path = None, controller = stem.control.Controller):
"""
Convenience function for quickly getting a control connection. This is very
@@ -145,6 +146,7 @@ def connect_port(control_addr = "127.0.0.1", control_port = 9051, password = Non
return _connect(control_port, password, chroot_path, controller)
+
def connect_socket_file(socket_path = "/var/run/tor/control", password = None, chroot_path = None, controller = stem.control.Controller):
"""
Convenience function for quickly getting a control connection. For more
@@ -167,6 +169,7 @@ def connect_socket_file(socket_path = "/var/run/tor/control", password = None, c
return _connect(control_socket, password, chroot_path, controller)
+
def _connect(control_socket, password, chroot_path, controller):
"""
Common implementation for the connect_* functions.
@@ -202,6 +205,7 @@ def _connect(control_socket, password, chroot_path, controller):
print "Unable to authenticate: %s" % exc
return None
+
def authenticate(controller, password = None, chroot_path = None, protocolinfo_response = None):
"""
Authenticates to a control socket using the information provided by a
@@ -412,6 +416,7 @@ def authenticate(controller, password = None, chroot_path = None, protocolinfo_r
raise AssertionError("BUG: Authentication failed without providing a recognized exception: %s" % str(auth_exceptions))
+
def authenticate_none(controller, suppress_ctl_errors = True):
"""
Authenticates to an open control socket. All control connections need to
@@ -458,6 +463,7 @@ def authenticate_none(controller, suppress_ctl_errors = True):
else:
raise OpenAuthRejected("Socket failed (%s)" % exc)
+
def authenticate_password(controller, password, suppress_ctl_errors = True):
"""
Authenticates to a control socket that uses a password (via the
@@ -527,6 +533,7 @@ def authenticate_password(controller, password, suppress_ctl_errors = True):
else:
raise PasswordAuthRejected("Socket failed (%s)" % exc)
+
def authenticate_cookie(controller, cookie_path, suppress_ctl_errors = True):
"""
Authenticates to a control socket that uses the contents of an authentication
@@ -605,6 +612,7 @@ def authenticate_cookie(controller, cookie_path, suppress_ctl_errors = True):
else:
raise CookieAuthRejected("Socket failed (%s)" % exc, cookie_path, False)
+
def authenticate_safecookie(controller, cookie_path, suppress_ctl_errors = True):
"""
Authenticates to a control socket using the safe cookie method, which is
@@ -750,6 +758,7 @@ def authenticate_safecookie(controller, cookie_path, suppress_ctl_errors = True)
else:
raise CookieAuthRejected(str(auth_response), cookie_path, True, auth_response)
+
def get_protocolinfo(controller):
"""
Issues a PROTOCOLINFO query to a control socket, getting information about
@@ -815,6 +824,7 @@ def get_protocolinfo(controller):
return protocolinfo_response
+
def _msg(controller, message):
"""
Sends and receives a message with either a
@@ -827,6 +837,7 @@ def _msg(controller, message):
else:
return controller.msg(message)
+
def _read_cookie(cookie_path, is_safecookie):
"""
Provides the contents of a given cookie file.
@@ -868,6 +879,7 @@ def _read_cookie(cookie_path, is_safecookie):
exc_msg = "Authentication failed: unable to read '%s' (%s)" % (cookie_path, exc)
raise UnreadableCookieFile(exc_msg, cookie_path, is_safecookie)
+
def _expand_cookie_path(protocolinfo_response, pid_resolver, pid_resolution_arg):
"""
Attempts to expand a relative cookie path with the given pid resolver. This
@@ -901,6 +913,7 @@ def _expand_cookie_path(protocolinfo_response, pid_resolver, pid_resolution_arg)
protocolinfo_response.cookie_path = cookie_path
+
class AuthenticationFailure(Exception):
"""
Base error for authentication failures.
@@ -913,6 +926,7 @@ class AuthenticationFailure(Exception):
super(AuthenticationFailure, self).__init__(message)
self.auth_response = auth_response
+
class UnrecognizedAuthMethods(AuthenticationFailure):
"""
All methods for authenticating aren't recognized.
@@ -924,27 +938,35 @@ class UnrecognizedAuthMethods(AuthenticationFailure):
super(UnrecognizedAuthMethods, self).__init__(message)
self.unknown_auth_methods = unknown_auth_methods
+
class IncorrectSocketType(AuthenticationFailure):
"Socket does not speak the control protocol."
+
class OpenAuthFailed(AuthenticationFailure):
"Failure to authenticate to an open socket."
+
class OpenAuthRejected(OpenAuthFailed):
"Attempt to connect to an open control socket was rejected."
+
class PasswordAuthFailed(AuthenticationFailure):
"Failure to authenticate with a password."
+
class PasswordAuthRejected(PasswordAuthFailed):
"Socket does not support password authentication."
+
class IncorrectPassword(PasswordAuthFailed):
"Authentication password incorrect."
+
class MissingPassword(PasswordAuthFailed):
"Password authentication is supported but we weren't provided with one."
+
class CookieAuthFailed(AuthenticationFailure):
"""
Failure to authenticate with an authentication cookie.
@@ -961,18 +983,23 @@ class CookieAuthFailed(AuthenticationFailure):
self.is_safecookie = is_safecookie
self.cookie_path = cookie_path
+
class CookieAuthRejected(CookieAuthFailed):
"Socket does not support password authentication."
+
class IncorrectCookieValue(CookieAuthFailed):
"Authentication cookie value was rejected."
+
class IncorrectCookieSize(CookieAuthFailed):
"Aborted because the cookie file is the wrong size."
+
class UnreadableCookieFile(CookieAuthFailed):
"Error arose in reading the authentication cookie."
+
class AuthChallengeFailed(CookieAuthFailed):
"""
AUTHCHALLENGE command has failed.
@@ -981,11 +1008,13 @@ class AuthChallengeFailed(CookieAuthFailed):
def __init__(self, message, cookie_path):
super(AuthChallengeFailed, self).__init__(message, cookie_path, True)
+
class AuthChallengeUnsupported(AuthChallengeFailed):
"""
AUTHCHALLENGE isn't implemented.
"""
+
class UnrecognizedAuthChallengeMethod(AuthChallengeFailed):
"""
Tor couldn't recognize our AUTHCHALLENGE method.
@@ -997,21 +1026,26 @@ class UnrecognizedAuthChallengeMethod(AuthChallengeFailed):
super(UnrecognizedAuthChallengeMethod, self).__init__(message, cookie_path)
self.authchallenge_method = authchallenge_method
+
class AuthSecurityFailure(AuthChallengeFailed):
"AUTHCHALLENGE response is invalid."
+
class InvalidClientNonce(AuthChallengeFailed):
"AUTHCHALLENGE request contains an invalid client nonce."
+
class MissingAuthInfo(AuthenticationFailure):
"""
The PROTOCOLINFO response didn't have enough information to authenticate.
These are valid control responses but really shouldn't happen in practice.
"""
+
class NoAuthMethods(MissingAuthInfo):
"PROTOCOLINFO response didn't have any methods for authenticating."
+
class NoAuthCookie(MissingAuthInfo):
"""
PROTOCOLINFO response supports cookie auth but doesn't have its path.
diff --git a/stem/control.py b/stem/control.py
index feb7834..2e82a65 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -210,6 +210,7 @@ GEOIP_FAILURE_THRESHOLD = 5
# changed to the more conventional is_alive() and current_thread() in python
# 2.6 and above. We should use that when dropping python 2.5 compatibility.
+
class BaseController(object):
"""
Controller for the tor process. This is a minimal base class for other
@@ -584,6 +585,7 @@ class BaseController(object):
self._event_notice.wait()
self._event_notice.clear()
+
class Controller(BaseController):
"""
Communicates with a control socket. This is built on top of the
@@ -1920,6 +1922,7 @@ class Controller(BaseController):
if not response.is_ok():
raise stem.ProtocolError("SETEVENTS received unexpected response\n%s" % response)
+
def _parse_circ_path(path):
"""
Parses a circuit path as a list of **(fingerprint, nickname)** tuples. Tor
@@ -1962,6 +1965,7 @@ def _parse_circ_path(path):
else:
return []
+
def _parse_circ_entry(entry):
"""
Parses a single relay's 'LongName' or 'ServerID'. See the
@@ -1998,6 +2002,7 @@ def _parse_circ_entry(entry):
return (fingerprint, nickname)
+
def _case_insensitive_lookup(entries, key, default = UNDEFINED):
"""
Makes a case insensitive lookup within a list or dictionary, providing the
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 0dba96c..97dc94a 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -55,6 +55,7 @@ Flag = stem.util.enum.Enum(
("VALID", "Valid"),
)
+
def parse_file(path, descriptor_file):
"""
Provides an iterator for the descriptors within a given file.
@@ -111,6 +112,7 @@ def parse_file(path, descriptor_file):
raise TypeError("Unable to determine the descriptor's type. filename: '%s', first line: '%s'" % (filename, first_line))
+
def _parse_metrics_file(descriptor_type, major_version, minor_version, descriptor_file):
# Parses descriptor files from metrics, yielding individual descriptors. This
# throws a TypeError if the descriptor_type or version isn't recognized.
@@ -141,6 +143,7 @@ def _parse_metrics_file(descriptor_type, major_version, minor_version, descripto
else:
raise TypeError("Unrecognized metrics descriptor format. type: '%s', version: '%i.%i'" % (descriptor_type, major_version, minor_version))
+
class Descriptor(object):
"""
Common parent for all types of descriptors.
@@ -176,6 +179,7 @@ class Descriptor(object):
def __str__(self):
return self._raw_contents
+
def _read_until_keywords(keywords, descriptor_file, inclusive = False, ignore_first = False, skip = False, end_position = None, include_ending_keyword = False):
"""
Reads from the descriptor file until we get to one of the given keywords or reach the
@@ -241,6 +245,7 @@ def _read_until_keywords(keywords, descriptor_file, inclusive = False, ignore_fi
else:
return content
+
def _get_pseudo_pgp_block(remaining_contents):
"""
Checks if given contents begins with a pseudo-Open-PGP-style block and, if
@@ -276,6 +281,7 @@ def _get_pseudo_pgp_block(remaining_contents):
else:
return None
+
def _get_descriptor_components(raw_contents, validate, extra_keywords = ()):
"""
Initial breakup of the server descriptor contents to make parsing easier.
diff --git a/stem/descriptor/export.py b/stem/descriptor/export.py
index becc72f..5be7dbc 100644
--- a/stem/descriptor/export.py
+++ b/stem/descriptor/export.py
@@ -15,9 +15,11 @@ import csv
import stem.descriptor
import stem.prereq
+
class _ExportDialect(csv.excel):
lineterminator = '\n'
+
def export_csv(descriptors, included_fields = (), excluded_fields = (), header = True):
"""
Provides a newline separated CSV for one or more descriptors. If simply
@@ -40,6 +42,7 @@ def export_csv(descriptors, included_fields = (), excluded_fields = (), header =
export_csv_file(output_buffer, descriptors, included_fields, excluded_fields, header)
return output_buffer.getvalue()
+
def export_csv_file(output_file, descriptors, included_fields = (), excluded_fields = (), header = True):
"""
Similar to :func:`stem.descriptor.export.export_csv`, except that the CSV is
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index 2c14c92..25ab615 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -133,6 +133,7 @@ SINGLE_FIELDS = (
"exit-streams-opened",
)
+
def parse_file(descriptor_file, validate = True):
"""
Iterates over the extra-info descriptors in a file.
@@ -161,6 +162,7 @@ def parse_file(descriptor_file, validate = True):
else:
break # done parsing file
+
def _parse_timestamp_and_interval(keyword, content):
"""
Parses a 'YYYY-MM-DD HH:MM:SS (NSEC s) *' entry.
@@ -194,6 +196,7 @@ def _parse_timestamp_and_interval(keyword, content):
except ValueError:
raise ValueError("%s line's timestamp wasn't parsable: %s" % (keyword, line))
+
class ExtraInfoDescriptor(stem.descriptor.Descriptor):
"""
Extra-info descriptor document.
@@ -791,6 +794,7 @@ class ExtraInfoDescriptor(stem.descriptor.Descriptor):
def _last_keyword(self):
return "router-signature"
+
class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
"""
Relay extra-info descriptor, constructed from data such as that provided by
@@ -838,6 +842,7 @@ class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
ExtraInfoDescriptor._parse(self, entries, validate)
+
class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
"""
Bridge extra-info descriptor (`bridge descriptor specification
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
index 28537c2..e8e3ae2 100644
--- a/stem/descriptor/networkstatus.py
+++ b/stem/descriptor/networkstatus.py
@@ -165,6 +165,7 @@ BANDWIDTH_WEIGHT_ENTRIES = (
"Wmb", "Wmd", "Wme", "Wmg", "Wmm",
)
+
def parse_file(document_file, validate = True, is_microdescriptor = False, document_version = 3):
"""
Parses a network status and iterates over the RouterStatusEntry in it. The
@@ -223,6 +224,7 @@ def parse_file(document_file, validate = True, is_microdescriptor = False, docum
for desc in desc_iterator:
yield desc
+
class NetworkStatusDocument(stem.descriptor.Descriptor):
"""
Common parent for network status documents.
@@ -235,6 +237,7 @@ class NetworkStatusDocument(stem.descriptor.Descriptor):
def get_unrecognized_lines(self):
return list(self._unrecognized_lines)
+
class NetworkStatusDocumentV2(NetworkStatusDocument):
"""
Version 2 network status document. These have been deprecated and are no
@@ -406,6 +409,7 @@ class NetworkStatusDocumentV2(NetworkStatusDocument):
if 'network-status-version' != entries.keys()[0]:
raise ValueError("Network status document (v2) are expected to start with a 'network-status-version' line:\n%s" % str(self))
+
class NetworkStatusDocumentV3(NetworkStatusDocument):
"""
Version 3 network status document. This could be either a vote or consensus.
@@ -524,6 +528,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument):
return str(self) > str(other)
+
class _DocumentHeader(object):
def __init__(self, document_file, validate, default_params):
self.version = None
@@ -733,6 +738,7 @@ class _DocumentHeader(object):
if value < minimum or value > maximum:
raise ValueError("'%s' value on the params line must be in the range of %i - %i, was %i" % (key, minimum, maximum, value))
+
class _DocumentFooter(object):
def __init__(self, document_file, validate, header):
self.signatures = []
@@ -796,6 +802,7 @@ class _DocumentFooter(object):
self.signatures.append(DocumentSignature(method, fingerprint, key_digest, block_contents, validate))
+
def _check_for_missing_and_disallowed_fields(header, entries, fields):
"""
Checks that we have mandatory fields for our type, and that we don't have
@@ -828,6 +835,7 @@ def _check_for_missing_and_disallowed_fields(header, entries, fields):
if disallowed_fields:
raise ValueError("Network status document has fields that shouldn't appear in this document type or version: %s" % ', '.join(disallowed_fields))
+
def _check_for_misordered_fields(entries, expected):
"""
To be valid a network status document's fiends need to appear in a specific
@@ -857,6 +865,7 @@ def _check_for_misordered_fields(entries, expected):
expected_label = ', '.join(expected)
raise ValueError("The fields in a section of the document are misordered. It should be '%s' but was '%s'" % (actual_label, expected_label))
+
def _parse_int_mappings(keyword, value, validate):
# Parse a series of 'key=value' entries, checking the following:
# - values are integers
@@ -895,6 +904,7 @@ def _parse_int_mappings(keyword, value, validate):
return results
+
class DirectoryAuthority(stem.descriptor.Descriptor):
"""
Directory authority information obtained from a v3 network status document.
@@ -1085,6 +1095,7 @@ class DirectoryAuthority(stem.descriptor.Descriptor):
return str(self) > str(other)
+
class KeyCertificate(stem.descriptor.Descriptor):
"""
Directory key certificate for a v3 network status document.
@@ -1245,6 +1256,7 @@ class KeyCertificate(stem.descriptor.Descriptor):
return str(self) > str(other)
+
class DocumentSignature(object):
"""
Directory signature of a v3 network status document.
diff --git a/stem/descriptor/reader.py b/stem/descriptor/reader.py
index 9f9f505..3373a1e 100644
--- a/stem/descriptor/reader.py
+++ b/stem/descriptor/reader.py
@@ -93,9 +93,11 @@ FINISHED = "DONE"
# dropping python 2.5 compatibility...
# http://docs.python.org/library/threading.html#threading.Event.is_set
+
class FileSkipped(Exception):
"Base error when we can't provide descriptor data from a file."
+
class AlreadyRead(FileSkipped):
"""
Already read a file with this 'last modified' timestamp or later.
@@ -110,6 +112,7 @@ class AlreadyRead(FileSkipped):
self.last_modified = last_modified
self.last_modified_when_read = last_modified_when_read
+
class ParsingFailure(FileSkipped):
"""
File contents could not be parsed as descriptor data.
@@ -121,6 +124,7 @@ class ParsingFailure(FileSkipped):
super(ParsingFailure, self).__init__()
self.exception = parsing_exception
+
class UnrecognizedType(FileSkipped):
"""
File doesn't contain descriptor data. This could either be due to its file
@@ -133,6 +137,7 @@ class UnrecognizedType(FileSkipped):
super(UnrecognizedType, self).__init__()
self.mime_type = mime_type
+
class ReadFailed(FileSkipped):
"""
An IOError occurred while trying to read the file.
@@ -145,12 +150,14 @@ class ReadFailed(FileSkipped):
super(ReadFailed, self).__init__()
self.exception = read_exception
+
class FileMissing(ReadFailed):
"File does not exist."
def __init__(self):
super(FileMissing, self).__init__(None)
+
def load_processed_files(path):
"""
Loads a dictionary of 'path => last modified timestamp' mappings, as
@@ -190,6 +197,7 @@ def load_processed_files(path):
return processed_files
+
def save_processed_files(path, processed_files):
"""
Persists a dictionary of 'path => last modified timestamp' mappings (as
@@ -222,6 +230,7 @@ def save_processed_files(path, processed_files):
output_file.write("%s %i\n" % (path, timestamp))
+
class DescriptorReader(object):
"""
Iterator for the descriptor data on the local file system. This can process
diff --git a/stem/descriptor/router_status_entry.py b/stem/descriptor/router_status_entry.py
index 6e048dd..0479bbd 100644
--- a/stem/descriptor/router_status_entry.py
+++ b/stem/descriptor/router_status_entry.py
@@ -22,6 +22,7 @@ import datetime
import stem.descriptor
import stem.exit_policy
+
def parse_file(document_file, validate, entry_class, entry_keyword = "r", start_position = None, end_position = None, section_end_keywords = (), extra_args = ()):
"""
Reads a range of the document_file containing some number of entry_class
@@ -88,6 +89,7 @@ def parse_file(document_file, validate, entry_class, entry_keyword = "r", start_
else:
break
+
class RouterStatusEntry(stem.descriptor.Descriptor):
"""
Information about an individual router stored within a network status
@@ -226,6 +228,7 @@ class RouterStatusEntry(stem.descriptor.Descriptor):
return str(self) > str(other)
+
class RouterStatusEntryV2(RouterStatusEntry):
"""
Information about an individual router stored within a version 2 network
@@ -269,6 +272,7 @@ class RouterStatusEntryV2(RouterStatusEntry):
return str(self) > str(other)
+
class RouterStatusEntryV3(RouterStatusEntry):
"""
Information about an individual router stored within a version 3 network
@@ -350,6 +354,7 @@ class RouterStatusEntryV3(RouterStatusEntry):
return str(self) > str(other)
+
class RouterStatusEntryMicroV3(RouterStatusEntry):
"""
Information about an individual router stored within a microdescriptor
@@ -411,6 +416,7 @@ class RouterStatusEntryMicroV3(RouterStatusEntry):
return str(self) > str(other)
+
def _parse_r_line(desc, value, validate, include_digest = True):
# Parses a RouterStatusEntry's 'r' line. They're very nearly identical for
# all current entry types (v2, v3, and microdescriptor v3) with one little
@@ -466,6 +472,7 @@ def _parse_r_line(desc, value, validate, include_digest = True):
if validate:
raise ValueError("Publication time time wasn't parsable: r %s" % value)
+
def _parse_a_line(desc, value, validate):
# "a" SP address ":" portlist
# example: a [2001:888:2133:0:82:94:251:204]:9001
@@ -499,6 +506,7 @@ def _parse_a_line(desc, value, validate):
desc.addresses_v6.setdefault(address, []).append((int(min_port), int(max_port)))
+
def _parse_s_line(desc, value, validate):
# "s" Flags
# example: s Named Running Stable Valid
@@ -513,6 +521,7 @@ def _parse_s_line(desc, value, validate):
elif flag == "":
raise ValueError("%s had extra whitespace on its 's' line: s %s" % (desc._name(), value))
+
def _parse_v_line(desc, value, validate):
# "v" version
# example: v Tor 0.2.2.35
@@ -530,6 +539,7 @@ def _parse_v_line(desc, value, validate):
if validate:
raise ValueError("%s has a malformed tor version (%s): v %s" % (desc._name(), exc, value))
+
def _parse_w_line(desc, value, validate):
# "w" "Bandwidth=" INT ["Measured=" INT]
# example: w Bandwidth=7980
@@ -572,6 +582,7 @@ def _parse_w_line(desc, value, validate):
else:
desc.unrecognized_bandwidth_entries.append(w_entry)
+
def _parse_p_line(desc, value, validate):
# "p" ("accept" / "reject") PortList
# p reject 1-65535
@@ -585,6 +596,7 @@ def _parse_p_line(desc, value, validate):
raise ValueError("%s exit policy is malformed (%s): p %s" % (desc._name(), exc, value))
+
def _parse_m_line(desc, value, validate):
# "m" methods 1*(algorithm "=" digest)
# example: m 8,9,10,11,12 sha256=g1vx9si329muxV3tquWIXXySNOIwRGMeAESKs/v4DWs
@@ -625,6 +637,7 @@ def _parse_m_line(desc, value, validate):
desc.microdescriptor_hashes.append((methods, hashes))
+
def _decode_fingerprint(identity, validate):
"""
Decodes the 'identity' value found in consensuses into the more common hex
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 037a4bd..6fe0e7c 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -71,6 +71,7 @@ SINGLE_FIELDS = (
"ntor-onion-key",
)
+
def parse_file(descriptor_file, validate = True):
"""
Iterates over the server descriptors in a file.
@@ -129,6 +130,7 @@ def parse_file(descriptor_file, validate = True):
else:
break # done parsing descriptors
+
class ServerDescriptor(stem.descriptor.Descriptor):
"""
Common parent for server descriptors.
@@ -607,6 +609,7 @@ class ServerDescriptor(stem.descriptor.Descriptor):
def _last_keyword(self):
return "router-signature"
+
class RelayDescriptor(ServerDescriptor):
"""
Server descriptor (`descriptor specification
@@ -806,6 +809,7 @@ class RelayDescriptor(ServerDescriptor):
return key_bytes
+
class BridgeDescriptor(ServerDescriptor):
"""
Bridge descriptor (`bridge descriptor specification
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index 56c68dc..d82892f 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -75,6 +75,7 @@ AddressType = stem.util.enum.Enum(("WILDCARD", "Wildcard"), ("IPv4", "IPv4"), ("
# some use cases where we might want to construct custom policies. Maybe make
# it a CustomExitPolicyRule subclass?
+
class ExitPolicy(object):
"""
Policy for the destinations that a relay allows or denies exiting to. This
@@ -259,6 +260,7 @@ class ExitPolicy(object):
else:
return False
+
class MicroExitPolicy(ExitPolicy):
"""
Exit policy provided by the microdescriptors. This is a distilled version of
@@ -338,6 +340,7 @@ class MicroExitPolicy(ExitPolicy):
else:
return False
+
class ExitPolicyRule(object):
"""
Single rule from the user's exit policy. These rules are chained together to
@@ -698,12 +701,15 @@ class ExitPolicyRule(object):
else:
return False
+
def _address_type_to_int(address_type):
return AddressType.index_of(address_type)
+
def _int_to_address_type(address_type_int):
return AddressType[AddressType.keys()[address_type_int]]
+
class MicroExitPolicyRule(ExitPolicyRule):
"""
Lighter weight ExitPolicyRule derivative for microdescriptors.
diff --git a/stem/prereq.py b/stem/prereq.py
index e3b4fdc..f9e53a7 100644
--- a/stem/prereq.py
+++ b/stem/prereq.py
@@ -26,6 +26,7 @@ from stem.util import log
IS_CRYPTO_AVAILABLE = None
+
def check_requirements():
"""
Checks that we meet the minimum requirements to run stem. If we don't then
@@ -41,6 +42,7 @@ def check_requirements():
elif major_version < 2 or minor_version < 5:
raise ImportError("stem requires python version 2.5 or greater")
+
def is_python_26():
"""
Checks if we're in the 2.6 - 2.x range.
@@ -50,6 +52,7 @@ def is_python_26():
return _check_version(6)
+
def is_python_27():
"""
Checks if we're in the 2.7 - 2.x range.
@@ -59,6 +62,7 @@ def is_python_27():
return _check_version(7)
+
def is_crypto_available():
global IS_CRYPTO_AVAILABLE
@@ -77,6 +81,7 @@ def is_crypto_available():
return IS_CRYPTO_AVAILABLE
+
def _check_version(minor_req):
major_version, minor_version = sys.version_info[0:2]
diff --git a/stem/process.py b/stem/process.py
index e654e85..64ee591 100644
--- a/stem/process.py
+++ b/stem/process.py
@@ -29,6 +29,7 @@ import stem.util.system
NO_TORRC = "<no torrc>"
DEFAULT_INIT_TIMEOUT = 90
+
def launch_tor(tor_cmd = "tor", args = None, torrc_path = None, completion_percent = 100, init_msg_handler = None, timeout = DEFAULT_INIT_TIMEOUT, take_ownership = False):
"""
Initializes a tor process. This blocks until initialization completes or we
@@ -170,6 +171,7 @@ def launch_tor(tor_cmd = "tor", args = None, torrc_path = None, completion_perce
last_problem = msg
+
def launch_tor_with_config(config, tor_cmd = "tor", completion_percent = 100, init_msg_handler = None, timeout = DEFAULT_INIT_TIMEOUT, take_ownership = False):
"""
Initializes a tor process, like :func:`~stem.process.launch_tor`, but with a
@@ -230,5 +232,6 @@ def launch_tor_with_config(config, tor_cmd = "tor", completion_percent = 100, in
except:
pass
+
def _get_pid():
return str(os.getpid())
diff --git a/stem/response/__init__.py b/stem/response/__init__.py
index dbf50f9..9544d71 100644
--- a/stem/response/__init__.py
+++ b/stem/response/__init__.py
@@ -53,6 +53,7 @@ KEY_ARG = re.compile("^(\S+)=")
CONTROL_ESCAPES = {r"\\": "\\", r"\"": "\"", r"\'": "'",
r"\r": "\r", r"\n": "\n", r"\t": "\t"}
+
def convert(response_type, message, **kwargs):
"""
Converts a :class:`~stem.response.ControlMessage` into a particular kind of
@@ -115,6 +116,7 @@ def convert(response_type, message, **kwargs):
message.__class__ = response_class
message._parse_message(**kwargs)
+
class ControlMessage(object):
"""
Message from the control socket. This is iterable and can be stringified for
@@ -226,6 +228,7 @@ class ControlMessage(object):
return ControlLine(self._parsed_content[index][2])
+
class ControlLine(str):
"""
String subclass that represents a line of controller output. This behaves as
@@ -384,6 +387,7 @@ class ControlLine(str):
self._remainder = remainder
return (key, next_entry)
+
def _parse_entry(line, quoted, escaped):
"""
Parses the next entry from the given space separated content.
@@ -425,6 +429,7 @@ def _parse_entry(line, quoted, escaped):
return (next_entry, remainder.lstrip())
+
def _get_quote_indices(line, escaped):
"""
Provides the indices of the next two quotes in the given content.
@@ -450,6 +455,7 @@ def _get_quote_indices(line, escaped):
return tuple(indices)
+
class SingleLineResponse(ControlMessage):
"""
Reply to a request that performs an action rather than querying data. These
diff --git a/stem/response/authchallenge.py b/stem/response/authchallenge.py
index cdc0ad4..161dd84 100644
--- a/stem/response/authchallenge.py
+++ b/stem/response/authchallenge.py
@@ -4,6 +4,7 @@ import stem.response
import stem.socket
import stem.util.tor_tools
+
class AuthChallengeResponse(stem.response.ControlMessage):
"""
AUTHCHALLENGE query response.
diff --git a/stem/response/events.py b/stem/response/events.py
index 51aa3d2..e9c3fdc 100644
--- a/stem/response/events.py
+++ b/stem/response/events.py
@@ -17,6 +17,7 @@ from stem.util import connection, log, str_tools, tor_tools
KW_ARG = re.compile("^(.*) ([A-Za-z0-9_]+)=(\S*)$")
QUOTED_KW_ARG = re.compile("^(.*) ([A-Za-z0-9_]+)=\"(.*)\"$")
+
class Event(stem.response.ControlMessage):
"""
Base for events we receive asynchronously, as described in section 4.1 of the
@@ -141,6 +142,7 @@ class Event(stem.response.ControlMessage):
unrecognized_msg = "%s event had an unrecognized %s (%s). Maybe a new addition to the control protocol? Full Event: '%s'" % (self.type, attr, value, self)
log.log_once(log_id, log.INFO, unrecognized_msg)
+
class AddrMapEvent(Event):
"""
Event that indicates a new address mapping.
@@ -174,6 +176,7 @@ class AddrMapEvent(Event):
if self.utc_expiry is not None:
self.utc_expiry = datetime.datetime.strptime(self.utc_expiry, "%Y-%m-%d %H:%M:%S")
+
class AuthDirNewDescEvent(Event):
"""
Event specific to directory authorities, indicating that we just received new
@@ -202,6 +205,7 @@ class AuthDirNewDescEvent(Event):
self.message = lines[2]
self.descriptor = '\n'.join(lines[3:-1])
+
class BandwidthEvent(Event):
"""
Event emitted every second with the bytes sent and received by tor.
@@ -226,6 +230,7 @@ class BandwidthEvent(Event):
self.read = long(self.read)
self.written = long(self.written)
+
class BuildTimeoutSetEvent(Event):
"""
Event indicating that the timeout value for a circuit has changed. This was
@@ -280,6 +285,7 @@ class BuildTimeoutSetEvent(Event):
self._log_if_unrecognized('set_type', stem.TimeoutSetType)
+
class CircuitEvent(Event):
"""
Event that indicates that a circuit has changed.
@@ -338,6 +344,7 @@ class CircuitEvent(Event):
self._log_if_unrecognized('reason', stem.CircClosureReason)
self._log_if_unrecognized('remote_reason', stem.CircClosureReason)
+
class CircMinorEvent(Event):
"""
Event providing information about minor changes in our circuits. This was
@@ -393,6 +400,7 @@ class CircMinorEvent(Event):
self._log_if_unrecognized('old_purpose', stem.CircPurpose)
self._log_if_unrecognized('old_hs_state', stem.HiddenServiceState)
+
class ClientsSeenEvent(Event):
"""
Periodic event on bridge relays that provides a summary of our users.
@@ -451,6 +459,7 @@ class ClientsSeenEvent(Event):
self.ip_versions = protocol_to_count
+
class ConfChangedEvent(Event):
"""
Event that indicates that our configuration changed, either in response to a
@@ -485,6 +494,7 @@ class ConfChangedEvent(Event):
self.config[key] = value
+
class DescChangedEvent(Event):
"""
Event that indicates that our descriptor has changed.
@@ -496,6 +506,7 @@ class DescChangedEvent(Event):
pass
+
class GuardEvent(Event):
"""
Event that indicates that our guard relays have changed.
@@ -516,6 +527,7 @@ class GuardEvent(Event):
_POSITIONAL_ARGS = ("guard_type", "name", "status")
+
class LogEvent(Event):
"""
Tor logging event. These are the most visible kind of event since, by
@@ -539,6 +551,7 @@ class LogEvent(Event):
self.message = str(self)[len(self.runlevel) + 1:].rstrip("\nOK")
+
class NetworkStatusEvent(Event):
"""
Event for when our copy of the consensus has changed. This was introduced in
@@ -561,6 +574,7 @@ class NetworkStatusEvent(Event):
entry_class = stem.descriptor.router_status_entry.RouterStatusEntryV3,
))
+
class NewConsensusEvent(Event):
"""
Event for when we have a new consensus. This is similar to
@@ -585,6 +599,7 @@ class NewConsensusEvent(Event):
entry_class = stem.descriptor.router_status_entry.RouterStatusEntryV3,
))
+
class NewDescEvent(Event):
"""
Event that indicates that a new descriptor is available.
@@ -603,6 +618,7 @@ class NewDescEvent(Event):
def _parse(self):
self.relays = tuple([stem.control._parse_circ_entry(entry) for entry in str(self).split()[1:]])
+
class ORConnEvent(Event):
"""
Event that indicates a change in a relay connection. The 'endpoint' could be
@@ -664,6 +680,7 @@ class ORConnEvent(Event):
self._log_if_unrecognized('status', stem.ORStatus)
self._log_if_unrecognized('reason', stem.ORClosureReason)
+
class SignalEvent(Event):
"""
Event that indicates that tor has received and acted upon a signal being sent
@@ -696,6 +713,7 @@ class SignalEvent(Event):
self._log_if_unrecognized('signal', expected_signals)
+
class StatusEvent(Event):
"""
Notification of a change in tor's state. These are generally triggered for
@@ -726,6 +744,7 @@ class StatusEvent(Event):
self._log_if_unrecognized('runlevel', stem.Runlevel)
+
class StreamEvent(Event):
"""
Event that indicates that a stream has changed.
@@ -796,6 +815,7 @@ class StreamEvent(Event):
self._log_if_unrecognized('remote_reason', stem.StreamClosureReason)
self._log_if_unrecognized('purpose', stem.StreamPurpose)
+
class StreamBwEvent(Event):
"""
Event (emitted approximately every second) with the bytes sent and received
diff --git a/stem/response/getconf.py b/stem/response/getconf.py
index c8b7da3..fff5380 100644
--- a/stem/response/getconf.py
+++ b/stem/response/getconf.py
@@ -1,6 +1,7 @@
import stem.response
import stem.socket
+
class GetConfResponse(stem.response.ControlMessage):
"""
Reply for a GETCONF query.
diff --git a/stem/response/getinfo.py b/stem/response/getinfo.py
index 3ef418b..3fcf6c3 100644
--- a/stem/response/getinfo.py
+++ b/stem/response/getinfo.py
@@ -1,6 +1,7 @@
import stem.response
import stem.socket
+
class GetInfoResponse(stem.response.ControlMessage):
"""
Reply for a GETINFO query.
diff --git a/stem/response/mapaddress.py b/stem/response/mapaddress.py
index d30ccfa..528f3ad 100644
--- a/stem/response/mapaddress.py
+++ b/stem/response/mapaddress.py
@@ -1,6 +1,7 @@
import stem.response
import stem.socket
+
class MapAddressResponse(stem.response.ControlMessage):
"""
Reply for a MAPADDRESS query.
diff --git a/stem/response/protocolinfo.py b/stem/response/protocolinfo.py
index 65a2d02..ba841d7 100644
--- a/stem/response/protocolinfo.py
+++ b/stem/response/protocolinfo.py
@@ -5,6 +5,7 @@ import stem.version
from stem.connection import AuthMethod
from stem.util import log
+
class ProtocolInfoResponse(stem.response.ControlMessage):
"""
Version one PROTOCOLINFO query response.
diff --git a/stem/socket.py b/stem/socket.py
index b23b247..ac21429 100644
--- a/stem/socket.py
+++ b/stem/socket.py
@@ -39,6 +39,7 @@ import stem.response
from stem.util import log
+
class ControlSocket(object):
"""
Wrapper for a socket connection that speaks the Tor control protocol. To the
@@ -282,6 +283,7 @@ class ControlSocket(object):
raise NotImplementedError("Unsupported Operation: this should be implemented by the ControlSocket subclass")
+
class ControlPort(ControlSocket):
"""
Control connection to tor. For more information see tor's ControlPort torrc
@@ -336,6 +338,7 @@ class ControlPort(ControlSocket):
except socket.error, exc:
raise stem.SocketError(exc)
+
class ControlSocketFile(ControlSocket):
"""
Control connection to tor. For more information see tor's ControlSocket torrc
@@ -379,6 +382,7 @@ class ControlSocketFile(ControlSocket):
except socket.error, exc:
raise stem.SocketError(exc)
+
def send_message(control_file, message, raw = False):
"""
Sends a message to the control socket, adding the expected formatting for
@@ -438,6 +442,7 @@ def send_message(control_file, message, raw = False):
log.info("Failed to send message: file has been closed")
raise stem.SocketClosed("file has been closed")
+
def recv_message(control_file):
"""
Pulls from a control socket until we either have a complete message or
@@ -557,6 +562,7 @@ def recv_message(control_file):
log.warn(prefix + "\"%s\" isn't a recognized divider type" % line)
raise stem.ProtocolError("Unrecognized divider type '%s': %s" % (divider, line))
+
def send_formatting(message):
"""
Performs the formatting expected from sent control messages. For more
diff --git a/stem/util/conf.py b/stem/util/conf.py
index 57f1fda..7e4cc8d 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -161,6 +161,7 @@ from stem.util import log
CONFS = {} # mapping of identifier to singleton instances of configs
+
class _SyncListener(object):
def __init__(self, config_dict, interceptor):
self.config_dict = config_dict
@@ -181,6 +182,7 @@ class _SyncListener(object):
self.config_dict[key] = new_value
+
def config_dict(handle, conf_mappings, handler = None):
"""
Makes a dictionary that stays synchronized with a configuration.
@@ -214,6 +216,7 @@ def config_dict(handle, conf_mappings, handler = None):
selected_config.add_listener(_SyncListener(conf_mappings, handler).update)
return conf_mappings
+
def get_config(handle):
"""
Singleton constructor for configuration file instances. If a configuration
@@ -228,6 +231,7 @@ def get_config(handle):
return CONFS[handle]
+
def parse_enum(key, value, enumeration):
"""
Provides the enumeration value for a given key. This is a case insensitive
@@ -244,6 +248,7 @@ def parse_enum(key, value, enumeration):
return parse_enum_csv(key, value, enumeration, 1)[0]
+
def parse_enum_csv(key, value, enumeration, count = None):
"""
Parses a given value as being a comma separated listing of enumeration keys,
@@ -303,6 +308,7 @@ def parse_enum_csv(key, value, enumeration, count = None):
return result
+
class Config(object):
"""
Handler for easily working with custom configurations, providing persistence
diff --git a/stem/util/connection.py b/stem/util/connection.py
index 6892bed..2a9c529 100644
--- a/stem/util/connection.py
+++ b/stem/util/connection.py
@@ -30,6 +30,7 @@ CRYPTOVARIABLE_EQUALITY_COMPARISON_NONCE = os.urandom(32)
FULL_IPv4_MASK = "255.255.255.255"
FULL_IPv6_MASK = "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
+
def is_valid_ip_address(address):
"""
Checks if a string is a valid IPv4 address.
@@ -56,6 +57,7 @@ def is_valid_ip_address(address):
return True
+
def is_valid_ipv6_address(address, allow_brackets = False):
"""
Checks if a string is a valid IPv6 address.
@@ -89,6 +91,7 @@ def is_valid_ipv6_address(address, allow_brackets = False):
return True
+
def is_valid_port(entry, allow_zero = False):
"""
Checks if a string or int is a valid port number.
@@ -118,6 +121,7 @@ def is_valid_port(entry, allow_zero = False):
return entry > 0 and entry < 65536
+
def expand_ipv6_address(address):
"""
Expands abbreviated IPv6 addresses to their full colon separated hex format.
@@ -156,6 +160,7 @@ def expand_ipv6_address(address):
return address
+
def get_mask(bits):
"""
Provides the IPv4 mask for a given number of bits, in the dotted-quad format.
@@ -181,6 +186,7 @@ def get_mask(bits):
# converts each octet into its integer value
return ".".join([str(int(octet, 2)) for octet in octets])
+
def get_masked_bits(mask):
"""
Provides the number of bits that an IPv4 subnet mask represents. Note that
@@ -205,6 +211,7 @@ def get_masked_bits(mask):
else:
raise ValueError("Unable to convert mask to a bit count: %s" % mask)
+
def get_mask_ipv6(bits):
"""
Provides the IPv6 mask for a given number of bits, in the hex colon-delimited
@@ -231,6 +238,7 @@ def get_mask_ipv6(bits):
# converts each group into its hex value
return ":".join(["%04x" % int(group, 2) for group in groupings]).upper()
+
def get_binary(value, bits):
"""
Provides the given value as a binary string, padded with zeros to the given
@@ -243,6 +251,7 @@ def get_binary(value, bits):
# http://www.daniweb.com/code/snippet216539.html
return "".join([str((value >> y) & 1) for y in range(bits - 1, -1, -1)])
+
def get_address_binary(address):
"""
Provides the binary value for an IPv4 or IPv6 address.
@@ -260,6 +269,7 @@ def get_address_binary(address):
else:
raise ValueError("'%s' is neither an IPv4 or IPv6 address" % address)
+
def hmac_sha256(key, msg):
"""
Generates a sha256 digest using the given key and message.
@@ -272,6 +282,7 @@ def hmac_sha256(key, msg):
return hmac.new(key, msg, hashlib.sha256).digest()
+
def cryptovariables_equal(x, y):
"""
Compares two strings for equality securely.
diff --git a/stem/util/enum.py b/stem/util/enum.py
index d22cd22..4617077 100644
--- a/stem/util/enum.py
+++ b/stem/util/enum.py
@@ -39,6 +39,7 @@ constructed as simple type listings...
import stem.util.str_tools
+
def UppercaseEnum(*args):
"""
Provides an :class:`~stem.util.enum.Enum` instance where the values are
@@ -59,6 +60,7 @@ def UppercaseEnum(*args):
return Enum(*[(v, v) for v in args])
+
class Enum(object):
"""
Basic enumeration.
diff --git a/stem/util/log.py b/stem/util/log.py
index 14b09df..04bcbaf 100644
--- a/stem/util/log.py
+++ b/stem/util/log.py
@@ -77,6 +77,7 @@ DEDUPLICATION_MESSAGE_IDS = set()
# could be found for logger "stem"' warning as per...
# http://docs.python.org/release/3.1.3/library/logging.html#configuring-logging-for-a-library
+
class _NullHandler(logging.Handler):
def emit(self, record):
pass
@@ -84,6 +85,7 @@ class _NullHandler(logging.Handler):
if not LOGGER.handlers:
LOGGER.addHandler(_NullHandler())
+
def get_logger():
"""
Provides the stem logger.
@@ -93,6 +95,7 @@ def get_logger():
return LOGGER
+
def logging_level(runlevel):
"""
Translates a runlevel into the value expected by the logging module.
@@ -105,6 +108,7 @@ def logging_level(runlevel):
else:
return logging.FATAL + 5
+
def escape(message):
"""
Escapes specific sequences for logging (newlines, tabs, carriage returns).
@@ -119,6 +123,7 @@ def escape(message):
return message
+
def log(runlevel, message):
"""
Logs a message at the given runlevel.
@@ -130,6 +135,7 @@ def log(runlevel, message):
if runlevel:
LOGGER.log(LOG_VALUES[runlevel], message)
+
def log_once(message_id, runlevel, message):
"""
Logs a message at the given runlevel. If a message with this ID has already
@@ -150,24 +156,31 @@ def log_once(message_id, runlevel, message):
# shorter aliases for logging at a runlevel
+
def trace(message):
log(Runlevel.TRACE, message)
+
def debug(message):
log(Runlevel.DEBUG, message)
+
def info(message):
log(Runlevel.INFO, message)
+
def notice(message):
log(Runlevel.NOTICE, message)
+
def warn(message):
log(Runlevel.WARN, message)
+
def error(message):
log(Runlevel.ERROR, message)
+
class LogBuffer(logging.Handler):
"""
Basic log handler that listens for stem events and stores them so they can be
@@ -198,6 +211,7 @@ class LogBuffer(logging.Handler):
def emit(self, record):
self._buffer.append(record)
+
class _StdoutLogger(logging.Handler):
def __init__(self, runlevel):
logging.Handler.__init__(self, level = logging_level(runlevel))
@@ -209,6 +223,7 @@ class _StdoutLogger(logging.Handler):
def emit(self, record):
print self.formatter.format(record)
+
def log_to_stdout(runlevel):
"""
Logs further events to stdout.
diff --git a/stem/util/ordereddict.py b/stem/util/ordereddict.py
index 778a534..b03ffb6 100644
--- a/stem/util/ordereddict.py
+++ b/stem/util/ordereddict.py
@@ -25,6 +25,7 @@
from UserDict import DictMixin
+
class OrderedDict(dict, DictMixin):
def __init__(self, *args, **kwds):
if len(args) > 1:
diff --git a/stem/util/proc.py b/stem/util/proc.py
index 436e061..e5be48d 100644
--- a/stem/util/proc.py
+++ b/stem/util/proc.py
@@ -62,6 +62,7 @@ Stat = stem.util.enum.Enum(
("CPU_STIME", "stime"), ("START_TIME", "start time")
)
+
def is_available():
"""
Checks if proc information is available on this platform.
@@ -88,6 +89,7 @@ def is_available():
return IS_PROC_AVAILABLE
+
def get_system_start_time():
"""
Provides the unix time (seconds since epoch) when the system started.
@@ -112,6 +114,7 @@ def get_system_start_time():
return SYS_START_TIME
+
def get_physical_memory():
"""
Provides the total physical memory on the system in bytes.
@@ -136,6 +139,7 @@ def get_physical_memory():
return SYS_PHYSICAL_MEMORY
+
def get_cwd(pid):
"""
Provides the current working directory for the given process.
@@ -163,6 +167,7 @@ def get_cwd(pid):
_log_runtime(parameter, proc_cwd_link, start_time)
return cwd
+
def get_uid(pid):
"""
Provides the user ID the given process is running under.
@@ -187,6 +192,7 @@ def get_uid(pid):
_log_failure(parameter, exc)
raise exc
+
def get_memory_usage(pid):
"""
Provides the memory usage in bytes for the given process.
@@ -219,6 +225,7 @@ def get_memory_usage(pid):
_log_failure(parameter, exc)
raise exc
+
def get_stats(pid, *stat_types):
"""
Provides process specific information. See the :data:`~stem.util.proc.Stat`
@@ -286,6 +293,7 @@ def get_stats(pid, *stat_types):
_log_runtime(parameter, stat_path, start_time)
return tuple(results)
+
def get_connections(pid):
"""
Queries connection related information from the proc contents. This provides
@@ -358,6 +366,7 @@ def get_connections(pid):
_log_runtime(parameter, "/proc/net/[tcp|udp]", start_time)
return conn
+
def _decode_proc_address_encoding(addr):
"""
Translates an address entry in the /proc/net/* contents to a human readable
@@ -396,6 +405,7 @@ def _decode_proc_address_encoding(addr):
return (ip, port)
+
def _is_float(*value):
try:
for v in value:
@@ -405,9 +415,11 @@ def _is_float(*value):
except ValueError:
return False
+
def _get_line(file_path, line_prefix, parameter):
return _get_lines(file_path, (line_prefix, ), parameter)[line_prefix]
+
def _get_lines(file_path, line_prefixes, parameter):
"""
Fetches lines with the given prefixes from a file. This only provides back
@@ -451,6 +463,7 @@ def _get_lines(file_path, line_prefixes, parameter):
_log_failure(parameter, exc)
raise exc
+
def _log_runtime(parameter, proc_location, start_time):
"""
Logs a message indicating a successful proc query.
@@ -463,6 +476,7 @@ def _log_runtime(parameter, proc_location, start_time):
runtime = time.time() - start_time
log.debug("proc call (%s): %s (runtime: %0.4f)" % (parameter, proc_location, runtime))
+
def _log_failure(parameter, exc):
"""
Logs a message indicating that the proc query failed.
diff --git a/stem/util/str_tools.py b/stem/util/str_tools.py
index 443bd4f..8527f2a 100644
--- a/stem/util/str_tools.py
+++ b/stem/util/str_tools.py
@@ -44,6 +44,7 @@ TIME_UNITS = (
(1.0, "s", " second"),
)
+
def to_camel_case(label, divider = "_", joiner = " "):
"""
Converts the given string to camel case, ie:
@@ -71,6 +72,7 @@ def to_camel_case(label, divider = "_", joiner = " "):
return joiner.join(words)
+
def get_size_label(byte_count, decimal = 0, is_long = False, is_bytes = True):
"""
Converts a number of bytes into a human readable label in its most
@@ -103,6 +105,7 @@ def get_size_label(byte_count, decimal = 0, is_long = False, is_bytes = True):
else:
return _get_label(SIZE_UNITS_BITS, byte_count, decimal, is_long)
+
def get_time_label(seconds, decimal = 0, is_long = False):
"""
Converts seconds into a time label truncated to its most significant units.
@@ -133,6 +136,7 @@ def get_time_label(seconds, decimal = 0, is_long = False):
return _get_label(TIME_UNITS, seconds, decimal, is_long)
+
def get_time_labels(seconds, is_long = False):
"""
Provides a list of label conversions for each time unit, starting with its
@@ -162,6 +166,7 @@ def get_time_labels(seconds, is_long = False):
return time_labels
+
def get_short_time_label(seconds):
"""
Provides a time in the following format:
@@ -201,6 +206,7 @@ def get_short_time_label(seconds):
return label
+
def parse_short_time_label(label):
"""
Provides the number of seconds corresponding to the formatting used for the
@@ -245,6 +251,7 @@ def parse_short_time_label(label):
except ValueError:
raise ValueError("Non-numeric value in time entry: %s" % label)
+
def parse_iso_timestamp(entry):
"""
Parses the ISO 8601 standard that provides for timestamps like...
@@ -277,6 +284,7 @@ def parse_iso_timestamp(entry):
timestamp = datetime.datetime.strptime(timestamp_str, "%Y-%m-%dT%H:%M:%S")
return timestamp + datetime.timedelta(microseconds = int(microseconds))
+
def _get_label(units, count, decimal, is_long):
"""
Provides label corresponding to units of the highest significance in the
diff --git a/stem/util/system.py b/stem/util/system.py
index acba9c1..760cc05 100644
--- a/stem/util/system.py
+++ b/stem/util/system.py
@@ -59,6 +59,7 @@ GET_CWD_PWDX = "pwdx %s"
GET_CWD_LSOF = "lsof -a -p %s -d cwd -Fn"
GET_BSD_JAIL_ID_PS = "ps -p %s -o jid"
+
def is_windows():
"""
Checks if we are running on Windows.
@@ -68,6 +69,7 @@ def is_windows():
return platform.system() == "Windows"
+
def is_mac():
"""
Checks if we are running on Mac OSX.
@@ -77,6 +79,7 @@ def is_mac():
return platform.system() == "Darwin"
+
def is_bsd():
"""
Checks if we are within the BSD family of operating systems. This presently
@@ -87,6 +90,7 @@ def is_bsd():
return platform.system() in ("Darwin", "FreeBSD", "OpenBSD")
+
def is_available(command, cached=True):
"""
Checks the current PATH to see if a command is available or not. If more
@@ -128,6 +132,7 @@ def is_available(command, cached=True):
CMD_AVAILABLE_CACHE[command] = cmd_exists
return cmd_exists
+
def is_running(command):
"""
Checks for if a process with a given name is running or not.
@@ -169,6 +174,7 @@ def is_running(command):
return None
+
def get_pid_by_name(process_name):
"""
Attempts to determine the process id for a running process, using...
@@ -288,6 +294,7 @@ def get_pid_by_name(process_name):
log.debug("failed to resolve a pid for '%s'" % process_name)
return None
+
def get_pid_by_port(port):
"""
Attempts to determine the process id for a process with the given port,
@@ -411,6 +418,7 @@ def get_pid_by_port(port):
return None # all queries failed
+
def get_pid_by_open_file(path):
"""
Attempts to determine the process id for a process with the given open file,
@@ -448,6 +456,7 @@ def get_pid_by_open_file(path):
return None # all queries failed
+
def get_cwd(pid):
"""
Provides the working directory of the given process.
@@ -515,6 +524,7 @@ def get_cwd(pid):
return None # all queries failed
+
def get_bsd_jail_id(pid):
"""
Gets the jail id for a process. These seem to only exist for FreeBSD (this
@@ -549,6 +559,7 @@ def get_bsd_jail_id(pid):
return 0
+
def expand_path(path, cwd = None):
"""
Provides an absolute path, expanding tildes with the user's home and
@@ -593,6 +604,7 @@ def expand_path(path, cwd = None):
return relative_path
+
def call(command, default = UNDEFINED):
"""
Issues a command in a subprocess, blocking until completion and returning the
diff --git a/stem/util/term.py b/stem/util/term.py
index d77fccf..dd2a8d2 100644
--- a/stem/util/term.py
+++ b/stem/util/term.py
@@ -55,6 +55,7 @@ ATTR_ENCODING = {Attr.BOLD: "1", Attr.UNDERLINE: "4", Attr.HILIGHT: "7"}
CSI = "\x1B[%sm"
RESET = CSI % "0"
+
def format(msg, *attr):
"""
Simple terminal text formatting using `ANSI escape sequences
diff --git a/stem/util/tor_tools.py b/stem/util/tor_tools.py
index 3f15eb2..59a3a59 100644
--- a/stem/util/tor_tools.py
+++ b/stem/util/tor_tools.py
@@ -32,6 +32,7 @@ FINGERPRINT_PATTERN = re.compile("^%s{40}$" % HEX_DIGIT)
NICKNAME_PATTERN = re.compile("^[a-zA-Z0-9]{1,19}$")
CIRC_ID_PATTERN = re.compile("^[a-zA-Z0-9]{1,16}$")
+
def is_valid_fingerprint(entry, check_prefix = False):
"""
Checks if a string is a properly formatted relay fingerprint. This checks for
@@ -54,6 +55,7 @@ def is_valid_fingerprint(entry, check_prefix = False):
return bool(FINGERPRINT_PATTERN.match(entry))
+
def is_valid_nickname(entry):
"""
Checks if a string is a valid format for being a nickname.
@@ -68,6 +70,7 @@ def is_valid_nickname(entry):
return bool(NICKNAME_PATTERN.match(entry))
+
def is_valid_circuit_id(entry):
"""
Checks if a string is a valid format for being a circuit identifier.
@@ -80,6 +83,7 @@ def is_valid_circuit_id(entry):
return bool(CIRC_ID_PATTERN.match(entry))
+
def is_valid_stream_id(entry):
"""
Checks if a string is a valid format for being a stream identifier.
@@ -90,6 +94,7 @@ def is_valid_stream_id(entry):
return is_valid_circuit_id(entry)
+
def is_hex_digits(entry, count):
"""
Checks if a string is the given number of hex digits. Digits represented by
diff --git a/stem/version.py b/stem/version.py
index 76b8a88..bbbd211 100644
--- a/stem/version.py
+++ b/stem/version.py
@@ -55,6 +55,7 @@ import stem.util.system
# cache for the get_system_tor_version function
VERSION_CACHE = {}
+
def get_system_tor_version(tor_cmd = "tor"):
"""
Queries tor for its version. This is os dependent, only working on linux,
@@ -94,6 +95,7 @@ def get_system_tor_version(tor_cmd = "tor"):
return VERSION_CACHE[tor_cmd]
+
class Version(object):
"""
Comparable tor version. These are constructed from strings that conform to
@@ -205,6 +207,7 @@ class Version(object):
else:
return 0
+
class VersionRequirements(object):
"""
Series of version constraints that can be compared to. For instance, this
diff --git a/test/check_whitespace.py b/test/check_whitespace.py
index 6bbbea4..63bc1c1 100644
--- a/test/check_whitespace.py
+++ b/test/check_whitespace.py
@@ -25,6 +25,7 @@ from stem.util import system
# if ran directly then run over everything one level up
DEFAULT_TARGET = os.path.sep.join(__file__.split(os.path.sep)[:-1])
+
def pep8_issues(base_path = DEFAULT_TARGET):
"""
Checks for stylistic issues that are an issue according to the parts of PEP8
@@ -62,7 +63,7 @@ def pep8_issues(base_path = DEFAULT_TARGET):
#
# Someone else can change this if they really care.
- ignored_issues = "E111,E121,W293,E501,E302,E251,E127"
+ ignored_issues = "E111,E121,W293,E501,E251,E127"
issues = {}
pep8_output = system.call("pep8 --ignore %s %s" % (ignored_issues, base_path))
@@ -76,6 +77,7 @@ def pep8_issues(base_path = DEFAULT_TARGET):
return issues
+
def get_issues(base_path = DEFAULT_TARGET):
"""
Checks python source code in the given directory for whitespace issues.
@@ -151,6 +153,7 @@ def get_issues(base_path = DEFAULT_TARGET):
return issues
+
def _get_files_with_suffix(base_path, suffix = ".py"):
"""
Iterates over files in a given directory, providing filenames with a certain
diff --git a/test/integ/connection/authentication.py b/test/integ/connection/authentication.py
index e707b49..50bc3c0 100644
--- a/test/integ/connection/authentication.py
+++ b/test/integ/connection/authentication.py
@@ -28,6 +28,7 @@ INCORRECT_COOKIE_FAIL = "Authentication failed: Authentication cookie did not ma
INCORRECT_SAFECOOKIE_FAIL = "Authentication failed: Safe cookie response did not match expected value."
INCORRECT_PASSWORD_FAIL = "Authentication failed: Password did not match HashedControlPassword value from configuration"
+
def _can_authenticate(auth_type):
"""
Checks if a given authentication method can authenticate to our control
@@ -56,6 +57,7 @@ def _can_authenticate(auth_type):
else:
return False
+
def _get_auth_failure_message(auth_type):
"""
Provides the message that tor will respond with if our current method of
@@ -96,6 +98,7 @@ def _get_auth_failure_message(auth_type):
raise ValueError("No methods of authentication. If this is an open socket then auth shouldn't fail.")
+
class TestAuthenticate(unittest.TestCase):
def setUp(self):
self.cookie_auth_methods = [stem.connection.AuthMethod.COOKIE]
diff --git a/test/integ/connection/connect.py b/test/integ/connection/connect.py
index 66b793d..e2d3d02 100644
--- a/test/integ/connection/connect.py
+++ b/test/integ/connection/connect.py
@@ -9,6 +9,7 @@ import unittest
import stem.connection
import test.runner
+
class TestConnect(unittest.TestCase):
def setUp(self):
# prevents the function from printing to the real stdout
diff --git a/test/integ/control/base_controller.py b/test/integ/control/base_controller.py
index 25af63d..2850bd9 100644
--- a/test/integ/control/base_controller.py
+++ b/test/integ/control/base_controller.py
@@ -14,6 +14,7 @@ import test.runner
import stem.socket
import stem.util.system
+
class StateObserver(object):
"""
Simple container for listening to ControlSocket state changes and
@@ -34,6 +35,7 @@ class StateObserver(object):
self.state = state
self.timestamp = timestamp
+
class TestBaseController(unittest.TestCase):
def test_connect_repeatedly(self):
"""
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index 6d91f50..a237f0a 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -27,6 +27,7 @@ import test.util
from stem.control import EventType
from stem.version import Requirement
+
class TestController(unittest.TestCase):
def test_from_port(self):
"""
diff --git a/test/integ/descriptor/__init__.py b/test/integ/descriptor/__init__.py
index 6553b2d..691d2f6 100644
--- a/test/integ/descriptor/__init__.py
+++ b/test/integ/descriptor/__init__.py
@@ -8,6 +8,7 @@ import os
DESCRIPTOR_TEST_DATA = os.path.join(os.path.dirname(__file__), "data")
+
def get_resource(filename):
"""
Provides the path for a file in our descriptor data directory.
diff --git a/test/integ/descriptor/extrainfo_descriptor.py b/test/integ/descriptor/extrainfo_descriptor.py
index 1293b3c..2ef5521 100644
--- a/test/integ/descriptor/extrainfo_descriptor.py
+++ b/test/integ/descriptor/extrainfo_descriptor.py
@@ -14,6 +14,7 @@ import test.runner
from stem.descriptor.extrainfo_descriptor import DirResponse
+
class TestExtraInfoDescriptor(unittest.TestCase):
def test_metrics_relay_descriptor(self):
"""
diff --git a/test/integ/descriptor/networkstatus.py b/test/integ/descriptor/networkstatus.py
index 862c166..5442d91 100644
--- a/test/integ/descriptor/networkstatus.py
+++ b/test/integ/descriptor/networkstatus.py
@@ -14,6 +14,7 @@ import stem.descriptor.networkstatus
import stem.version
import test.integ.descriptor
+
class TestNetworkStatus(unittest.TestCase):
def test_cached_consensus(self):
"""
diff --git a/test/integ/descriptor/reader.py b/test/integ/descriptor/reader.py
index ba807f0..ec0d87e 100644
--- a/test/integ/descriptor/reader.py
+++ b/test/integ/descriptor/reader.py
@@ -27,9 +27,11 @@ DESCRIPTOR_TEST_DATA = os.path.join(my_dir, "data")
TAR_DESCRIPTORS = None
+
def _get_processed_files_path():
return test.runner.get_runner().get_test_dir("descriptor_processed_files")
+
def _make_processed_files_listing(contents):
"""
Writes the given 'processed file' listing to disk, returning the path where
@@ -44,6 +46,7 @@ def _make_processed_files_listing(contents):
return test_listing_path
+
def _get_raw_tar_descriptors():
global TAR_DESCRIPTORS
@@ -71,6 +74,7 @@ def _get_raw_tar_descriptors():
return TAR_DESCRIPTORS
+
class SkipListener:
def __init__(self):
self.results = [] # (path, exception) tuples that we've received
@@ -78,6 +82,7 @@ class SkipListener:
def listener(self, path, exception):
self.results.append((path, exception))
+
class TestDescriptorReader(unittest.TestCase):
def tearDown(self):
# cleans up 'processed file' listings that we made
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
index b6b8e62..8cf0bd1 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -15,6 +15,7 @@ import stem.version
import test.integ.descriptor
import test.runner
+
class TestServerDescriptor(unittest.TestCase):
def test_metrics_descriptor(self):
"""
diff --git a/test/integ/process.py b/test/integ/process.py
index 6629575..0612163 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -20,6 +20,7 @@ from test import mocking
DATA_DIRECTORY = '/tmp/stem_integ'
+
def _kill_process(process):
if stem.prereq.is_python_26():
process.kill()
@@ -28,6 +29,7 @@ def _kill_process(process):
process.communicate() # block until its definitely gone
+
class TestProcess(unittest.TestCase):
def setUp(self):
if not os.path.exists(DATA_DIRECTORY):
diff --git a/test/integ/response/protocolinfo.py b/test/integ/response/protocolinfo.py
index 623ce48..b309e91 100644
--- a/test/integ/response/protocolinfo.py
+++ b/test/integ/response/protocolinfo.py
@@ -16,6 +16,7 @@ import test.runner
from test import mocking
from test.integ.util.system import filter_system_call
+
class TestProtocolInfo(unittest.TestCase):
def setUp(self):
mocking.mock(stem.util.proc.is_available, mocking.return_false())
diff --git a/test/integ/socket/control_message.py b/test/integ/socket/control_message.py
index 5c78f1a..ccceb26 100644
--- a/test/integ/socket/control_message.py
+++ b/test/integ/socket/control_message.py
@@ -11,6 +11,7 @@ import stem.socket
import stem.version
import test.runner
+
class TestControlMessage(unittest.TestCase):
def test_unestablished_socket(self):
"""
diff --git a/test/integ/socket/control_socket.py b/test/integ/socket/control_socket.py
index 13daaf4..0d6dd36 100644
--- a/test/integ/socket/control_socket.py
+++ b/test/integ/socket/control_socket.py
@@ -17,6 +17,7 @@ import stem.control
import stem.socket
import test.runner
+
class TestControlSocket(unittest.TestCase):
def test_send_buffered(self):
"""
diff --git a/test/integ/util/conf.py b/test/integ/util/conf.py
index 6fedca1..6eb1cbb 100644
--- a/test/integ/util/conf.py
+++ b/test/integ/util/conf.py
@@ -45,9 +45,11 @@ what a beautiful day.
Why are those arrows",
coming my way?!?"""
+
def _get_test_config_path():
return test.runner.get_runner().get_test_dir("integ_test_cfg")
+
def _make_config(contents):
"""
Writes a test configuration to disk, returning the path where it is located.
@@ -62,6 +64,7 @@ def _make_config(contents):
return test_config_path
+
class TestConf(unittest.TestCase):
def tearDown(self):
# clears the config contents
diff --git a/test/integ/util/proc.py b/test/integ/util/proc.py
index 557c7a8..2282831 100644
--- a/test/integ/util/proc.py
+++ b/test/integ/util/proc.py
@@ -12,6 +12,7 @@ import test.runner
from stem.util import proc
+
class TestProc(unittest.TestCase):
def test_get_cwd(self):
"""
diff --git a/test/integ/util/system.py b/test/integ/util/system.py
index 0564118..f6601fc 100644
--- a/test/integ/util/system.py
+++ b/test/integ/util/system.py
@@ -13,6 +13,7 @@ import test.runner
from test import mocking
+
def filter_system_call(prefixes):
"""
Provides a functor that passes calls on to the stem.util.system.call()
@@ -27,6 +28,7 @@ def filter_system_call(prefixes):
return _filter_system_call
+
def _has_port():
"""
True if our test runner has a control port, False otherwise.
@@ -34,6 +36,7 @@ def _has_port():
return test.runner.Torrc.PORT in test.runner.get_runner().get_options()
+
class TestSystem(unittest.TestCase):
is_extra_tor_running = None
diff --git a/test/integ/version.py b/test/integ/version.py
index 9542c63..b40537c 100644
--- a/test/integ/version.py
+++ b/test/integ/version.py
@@ -9,6 +9,7 @@ import stem.prereq
import stem.version
import test.runner
+
class TestVersion(unittest.TestCase):
def test_get_system_tor_version(self):
"""
diff --git a/test/mocking.py b/test/mocking.py
index 1ebe88d..df545dc 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -192,27 +192,33 @@ NETWORK_STATUS_DOCUMENT_FOOTER = (
("directory-signature", "%s %s\n%s" % (DOC_SIG.identity, DOC_SIG.key_digest, DOC_SIG.signature)),
)
+
def no_op():
def _no_op(*args):
pass
return _no_op
+
def return_value(value):
def _return_value(*args):
return value
return _return_value
+
def return_true():
return return_value(True)
+
def return_false():
return return_value(False)
+
def return_none():
return return_value(None)
+
def return_for_args(args_to_return_value, default = None, is_method = False):
"""
Returns a value if the arguments to it match something in a given
@@ -274,12 +280,14 @@ def return_for_args(args_to_return_value, default = None, is_method = False):
return _return_value
+
def raise_exception(exception):
def _raise(*args):
raise exception
return _raise
+
def support_with(obj):
"""
Provides no-op support for the 'with' keyword, adding __enter__ and __exit__
@@ -295,6 +303,7 @@ def support_with(obj):
obj.__dict__["__exit__"] = no_op()
return obj
+
def mock(target, mock_call, target_module=None):
"""
Mocks the given function, saving the initial implementation so it can be
@@ -330,6 +339,7 @@ def mock(target, mock_call, target_module=None):
else:
setattr(target_module, target.__name__, mock_call)
+
def mock_method(target_class, method_name, mock_call):
"""
Mocks the given method in target_class in a similar fashion as mock()
@@ -375,6 +385,7 @@ def mock_method(target_class, method_name, mock_call):
# mocks the function with this wrapper
setattr(target_class, method_name, mock_wrapper)
+
def revert_mocking():
"""
Reverts any mocking done by this function.
@@ -399,6 +410,7 @@ def revert_mocking():
MOCK_STATE.clear()
+
def get_real_function(function):
"""
Provides the original, non-mocked implementation for a function or method.
@@ -415,6 +427,7 @@ def get_real_function(function):
else:
return function
+
def get_all_combinations(attr, include_empty = False):
"""
Provides an iterator for all combinations of a set of attributes. For
@@ -451,6 +464,7 @@ def get_all_combinations(attr, include_empty = False):
seen.add(item)
yield item
+
def get_object(object_class, methods = None):
"""
Provides a mock instance of an arbitrary class. Its methods are mocked with
@@ -482,6 +496,7 @@ def get_object(object_class, methods = None):
return mock_class()
+
def get_message(content, reformat = True):
"""
Provides a ControlMessage with content modified to be parsable. This makes
@@ -504,6 +519,7 @@ def get_message(content, reformat = True):
return stem.socket.recv_message(StringIO.StringIO(content))
+
def get_protocolinfo_response(**attributes):
"""
Provides a ProtocolInfoResponse, customized with the given attributes. The
@@ -523,6 +539,7 @@ def get_protocolinfo_response(**attributes):
return protocolinfo_response
+
def _get_descriptor_content(attr = None, exclude = (), header_template = (), footer_template = ()):
"""
Constructs a minimal descriptor with the given attributes. The content we
@@ -595,6 +612,7 @@ def _get_descriptor_content(attr = None, exclude = (), header_template = (), foo
return "\n".join(header_content + remainder + footer_content)
+
def get_relay_server_descriptor(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -615,6 +633,7 @@ def get_relay_server_descriptor(attr = None, exclude = (), content = False):
desc_content = sign_descriptor_content(desc_content)
return stem.descriptor.server_descriptor.RelayDescriptor(desc_content, validate = True)
+
def get_bridge_server_descriptor(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -634,6 +653,7 @@ def get_bridge_server_descriptor(attr = None, exclude = (), content = False):
else:
return stem.descriptor.server_descriptor.BridgeDescriptor(desc_content, validate = True)
+
def get_relay_extrainfo_descriptor(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -653,6 +673,7 @@ def get_relay_extrainfo_descriptor(attr = None, exclude = (), content = False):
else:
return stem.descriptor.extrainfo_descriptor.RelayExtraInfoDescriptor(desc_content, validate = True)
+
def get_bridge_extrainfo_descriptor(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -672,6 +693,7 @@ def get_bridge_extrainfo_descriptor(attr = None, exclude = (), content = False):
else:
return stem.descriptor.extrainfo_descriptor.BridgeExtraInfoDescriptor(desc_content, validate = True)
+
def get_router_status_entry_v2(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -691,6 +713,7 @@ def get_router_status_entry_v2(attr = None, exclude = (), content = False):
else:
return stem.descriptor.router_status_entry.RouterStatusEntryV2(desc_content, validate = True)
+
def get_router_status_entry_v3(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -710,6 +733,7 @@ def get_router_status_entry_v3(attr = None, exclude = (), content = False):
else:
return stem.descriptor.router_status_entry.RouterStatusEntryV3(desc_content, validate = True)
+
def get_router_status_entry_micro_v3(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -729,6 +753,7 @@ def get_router_status_entry_micro_v3(attr = None, exclude = (), content = False)
else:
return stem.descriptor.router_status_entry.RouterStatusEntryMicroV3(desc_content, validate = True)
+
def get_directory_authority(attr = None, exclude = (), is_vote = False, content = False):
"""
Provides the descriptor content for...
@@ -760,6 +785,7 @@ def get_directory_authority(attr = None, exclude = (), is_vote = False, content
else:
return stem.descriptor.networkstatus.DirectoryAuthority(desc_content, validate = True, is_vote = is_vote)
+
def get_key_certificate(attr = None, exclude = (), content = False):
"""
Provides the descriptor content for...
@@ -779,6 +805,7 @@ def get_key_certificate(attr = None, exclude = (), content = False):
else:
return stem.descriptor.networkstatus.KeyCertificate(desc_content, validate = True)
+
def get_network_status_document_v2(attr = None, exclude = (), routers = None, content = False):
"""
Provides the descriptor content for...
@@ -799,6 +826,7 @@ def get_network_status_document_v2(attr = None, exclude = (), routers = None, co
else:
return stem.descriptor.networkstatus.NetworkStatusDocumentV2(desc_content, validate = True)
+
def get_network_status_document_v3(attr = None, exclude = (), authorities = None, routers = None, content = False):
"""
Provides the descriptor content for...
@@ -850,6 +878,7 @@ def get_network_status_document_v3(attr = None, exclude = (), authorities = None
else:
return stem.descriptor.networkstatus.NetworkStatusDocumentV3(desc_content, validate = True)
+
def sign_descriptor_content(desc_content):
"""
Add a valid signature to the supplied descriptor string.
diff --git a/test/network.py b/test/network.py
index cab998a..74b4832 100644
--- a/test/network.py
+++ b/test/network.py
@@ -27,9 +27,11 @@ SOCKS5_NOAUTH_RESPONSE = (0x05, 0x00)
SOCKS5_CONN_BY_IPV4 = (0x05, 0x01, 0x00, 0x01)
SOCKS5_CONN_BY_NAME = (0x05, 0x01, 0x00, 0x03)
+
class ProxyError(Exception):
"Base error for proxy issues."
+
class SocksError(ProxyError):
"""
Exception raised for any problems returned by the SOCKS proxy.
@@ -59,6 +61,7 @@ class SocksError(ProxyError):
code = self.code
return "[%s] %s" % (code, self._ERROR_MESSAGE[code])
+
class Socks(_socket_socket):
"""
A **socket.socket**-like interface through a SOCKS5 proxy connection.
@@ -218,6 +221,7 @@ class Socks(_socket_socket):
raise NotImplementedError
+
class SocksPatch(object):
"""
Monkey-patch **socket.socket** to use :class:`~test.network.Socks`, instead.
diff --git a/test/output.py b/test/output.py
index cadf071..0a3f9da 100644
--- a/test/output.py
+++ b/test/output.py
@@ -36,22 +36,26 @@ LINE_ATTR = {
LineType.CONTENT: (term.Color.CYAN,),
}
+
def print_line(msg, *attr):
if CONFIG["argument.no_color"]:
print msg
else:
print term.format(msg, *attr)
+
def print_noline(msg, *attr):
if CONFIG["argument.no_color"]:
sys.stdout.write(msg)
else:
sys.stdout.write(term.format(msg, *attr))
+
def print_divider(msg, is_header = False):
attr = HEADER_ATTR if is_header else CATEGORY_ATTR
print_line("%s\n%s\n%s\n" % (DIVIDER, msg.center(70), DIVIDER), *attr)
+
def print_logging(logging_buffer):
if not logging_buffer.is_empty():
for entry in logging_buffer:
@@ -59,6 +63,7 @@ def print_logging(logging_buffer):
print
+
def print_config(test_config):
print_divider("TESTING CONFIG", True)
print_line("Test configuration... ", term.Color.BLUE, term.Attr.BOLD)
@@ -74,6 +79,7 @@ def print_config(test_config):
print
+
def apply_filters(testing_output, *filters):
"""
Gets the tests results, possibly processed through a series of filters. The
@@ -111,6 +117,7 @@ def apply_filters(testing_output, *filters):
return "\n".join(results) + "\n"
+
def colorize(line_type, line_content):
"""
Applies escape sequences so each line is colored according to its type.
@@ -121,6 +128,7 @@ def colorize(line_type, line_content):
else:
return term.format(line_content, *LINE_ATTR[line_type])
+
def strip_module(line_type, line_content):
"""
Removes the module name from testing output. This information tends to be
@@ -134,6 +142,7 @@ def strip_module(line_type, line_content):
return line_content
+
def align_results(line_type, line_content):
"""
Strips the normal test results, and adds a right aligned variant instead with
@@ -168,6 +177,7 @@ def align_results(line_type, line_content):
else:
return "%-61s[%s]" % (line_content, term.format(new_ending, term.Attr.BOLD))
+
class ErrorTracker(object):
"""
Stores any failure or error results we've encountered.
diff --git a/test/prompt.py b/test/prompt.py
index afbd743..2d5219d 100644
--- a/test/prompt.py
+++ b/test/prompt.py
@@ -27,6 +27,7 @@ CONTROL_PORT = 2779
STOP_CONFIRMATION = "Would you like to stop the tor instance we made? (y/n, default: n): "
+
def print_usage():
"""
Provides a welcoming message.
@@ -36,6 +37,7 @@ def print_usage():
print "via the 'controller' variable."
print
+
def start():
"""
Starts up a tor instance that we can attach a controller to.
@@ -51,6 +53,7 @@ def start():
stem.process.launch_tor_with_config(config = tor_config, completion_percent = 5)
sys.stdout.write(" done\n\n")
+
def stop(prompt = False):
"""
Stops the tor instance spawned by this module.
@@ -69,6 +72,7 @@ def stop(prompt = False):
os.kill(tor_pid, signal.SIGTERM)
+
def is_running():
"""
Checks if we're likely running a tor instance spawned by this module. This is
@@ -80,6 +84,7 @@ def is_running():
return bool(stem.util.system.get_pid_by_port(CONTROL_PORT))
+
def controller():
"""
Provides a Controller for our tor instance. This starts tor if it isn't
diff --git a/test/runner.py b/test/runner.py
index dbfed24..bba9e78 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -100,12 +100,15 @@ Torrc = stem.util.enum.Enum(
# (test_instance, test_name) tuples that we've registered as having been ran
RAN_TESTS = []
+
class RunnerStopped(Exception):
"Raised when we try to use a Runner that doesn't have an active tor instance"
+
class TorInaccessable(Exception):
"Raised when information is needed from tor but the instance we have is inaccessible"
+
def skip(test_case, message):
"""
Skips the test if we can. The capability for skipping tests was added in
@@ -119,6 +122,7 @@ def skip(test_case, message):
if stem.prereq.is_python_27():
test_case.skipTest(message)
+
def require_control(test_case):
"""
Skips the test unless tor provides an endpoint for controllers to attach to.
@@ -132,6 +136,7 @@ def require_control(test_case):
skip(test_case, "(no connection)")
return True
+
def require_version(test_case, req_version):
"""
Skips the test unless we meet the required version.
@@ -146,6 +151,7 @@ def require_version(test_case, req_version):
skip(test_case, "(requires %s)" % req_version)
return True
+
def require_online(test_case):
"""
Skips the test if we weren't started with the ONLINE target, which indicates
@@ -160,6 +166,7 @@ def require_online(test_case):
skip(test_case, "(requires online target)")
return True
+
def only_run_once(test_case, test_name):
"""
Skips the test if it has ran before. If it hasn't then flags it as being ran.
@@ -178,6 +185,7 @@ def only_run_once(test_case, test_name):
else:
RAN_TESTS.append((test_case, test_name))
+
def exercise_controller(test_case, controller):
"""
Checks that we can now use the socket by issuing a 'GETINFO config-file'
@@ -199,6 +207,7 @@ def exercise_controller(test_case, controller):
test_case.assertEquals("config-file=%s\nOK" % torrc_path, str(config_file_response))
+
def get_runner():
"""
Singleton for the runtime context of integration tests.
@@ -213,6 +222,7 @@ def get_runner():
return INTEG_RUNNER
+
class _MockChrootFile(object):
"""
Wrapper around a file object that strips given content from readline()
@@ -227,6 +237,7 @@ class _MockChrootFile(object):
def readline(self):
return self.wrapped_file.readline().replace(self.strip_text, "")
+
class Runner(object):
def __init__(self):
self._runner_lock = threading.RLock()
diff --git a/test/unit/connection/authentication.py b/test/unit/connection/authentication.py
index 0c05f21..05818b4 100644
--- a/test/unit/connection/authentication.py
+++ b/test/unit/connection/authentication.py
@@ -16,6 +16,7 @@ import stem.connection
from stem.util import log
from test import mocking
+
class TestAuthenticate(unittest.TestCase):
def setUp(self):
mocking.mock(stem.connection.get_protocolinfo, mocking.no_op())
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index 34b98a5..7cb14b7 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -14,6 +14,7 @@ from stem.control import _parse_circ_path, Controller, EventType
from stem.response import events
from test import mocking
+
class TestControl(unittest.TestCase):
def setUp(self):
socket = stem.socket.ControlSocket()
diff --git a/test/unit/descriptor/export.py b/test/unit/descriptor/export.py
index bd25c6e..12c6e56 100644
--- a/test/unit/descriptor/export.py
+++ b/test/unit/descriptor/export.py
@@ -11,6 +11,7 @@ import test.runner
from stem.descriptor.export import export_csv, export_csv_file
from test.mocking import get_relay_server_descriptor, get_bridge_server_descriptor
+
class TestExport(unittest.TestCase):
def test_minimal_descriptor(self):
"""
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index 4ef0276..382aaee 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -8,6 +8,7 @@ import unittest
from stem.descriptor.extrainfo_descriptor import RelayExtraInfoDescriptor, DirResponse, DirStat
from test.mocking import get_relay_extrainfo_descriptor, get_bridge_extrainfo_descriptor, CRYPTO_BLOB
+
class TestExtraInfoDescriptor(unittest.TestCase):
def test_minimal_extrainfo_descriptor(self):
"""
diff --git a/test/unit/descriptor/networkstatus/directory_authority.py b/test/unit/descriptor/networkstatus/directory_authority.py
index 68e4713..f3d7fe1 100644
--- a/test/unit/descriptor/networkstatus/directory_authority.py
+++ b/test/unit/descriptor/networkstatus/directory_authority.py
@@ -9,6 +9,7 @@ import test.runner
from stem.descriptor.networkstatus import DirectoryAuthority
from test.mocking import get_directory_authority, get_key_certificate, AUTHORITY_HEADER
+
class TestDirectoryAuthority(unittest.TestCase):
def test_minimal_consensus_authority(self):
"""
diff --git a/test/unit/descriptor/networkstatus/document_v2.py b/test/unit/descriptor/networkstatus/document_v2.py
index 88561e6..9412cf9 100644
--- a/test/unit/descriptor/networkstatus/document_v2.py
+++ b/test/unit/descriptor/networkstatus/document_v2.py
@@ -7,6 +7,7 @@ import unittest
from test.mocking import get_network_status_document_v2, NETWORK_STATUS_DOCUMENT_HEADER_V2, NETWORK_STATUS_DOCUMENT_FOOTER_V2
+
class TestNetworkStatusDocument(unittest.TestCase):
def test_minimal_document(self):
"""
diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py
index 0ab5694..f2d0bf1 100644
--- a/test/unit/descriptor/networkstatus/document_v3.py
+++ b/test/unit/descriptor/networkstatus/document_v3.py
@@ -33,6 +33,7 @@ from test.mocking import support_with, \
DOC_SIG, \
NETWORK_STATUS_DOCUMENT_FOOTER
+
class TestNetworkStatusDocument(unittest.TestCase):
def test_minimal_consensus(self):
"""
diff --git a/test/unit/descriptor/networkstatus/key_certificate.py b/test/unit/descriptor/networkstatus/key_certificate.py
index fdd90c5..c91c9e3 100644
--- a/test/unit/descriptor/networkstatus/key_certificate.py
+++ b/test/unit/descriptor/networkstatus/key_certificate.py
@@ -12,6 +12,7 @@ from test.mocking import get_key_certificate, \
KEY_CERTIFICATE_HEADER, \
KEY_CERTIFICATE_FOOTER
+
class TestKeyCertificate(unittest.TestCase):
def test_minimal(self):
"""
diff --git a/test/unit/descriptor/reader.py b/test/unit/descriptor/reader.py
index 4ae0aff..985c129 100644
--- a/test/unit/descriptor/reader.py
+++ b/test/unit/descriptor/reader.py
@@ -8,6 +8,7 @@ import unittest
import stem.descriptor.reader
import test.mocking as mocking
+
class TestDescriptorReader(unittest.TestCase):
def tearDown(self):
mocking.revert_mocking()
diff --git a/test/unit/descriptor/router_status_entry.py b/test/unit/descriptor/router_status_entry.py
index 9565702..e35174c 100644
--- a/test/unit/descriptor/router_status_entry.py
+++ b/test/unit/descriptor/router_status_entry.py
@@ -15,6 +15,7 @@ from test.mocking import get_router_status_entry_v2, \
get_router_status_entry_micro_v3, \
ROUTER_STATUS_ENTRY_V3_HEADER
+
class TestRouterStatusEntry(unittest.TestCase):
def test_fingerprint_decoding(self):
"""
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index e3f4e78..bb49c02 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -18,6 +18,7 @@ from test.mocking import get_relay_server_descriptor, \
CRYPTO_BLOB, \
sign_descriptor_content
+
class TestServerDescriptor(unittest.TestCase):
def test_minimal_relay_descriptor(self):
"""
diff --git a/test/unit/exit_policy/policy.py b/test/unit/exit_policy/policy.py
index dce4cb7..f4cc499 100644
--- a/test/unit/exit_policy/policy.py
+++ b/test/unit/exit_policy/policy.py
@@ -8,6 +8,7 @@ from stem.exit_policy import ExitPolicy, \
MicroExitPolicy, \
ExitPolicyRule
+
class TestExitPolicy(unittest.TestCase):
def test_example(self):
# tests the ExitPolicy and MicroExitPolicy pydoc examples
diff --git a/test/unit/exit_policy/rule.py b/test/unit/exit_policy/rule.py
index 538418d..176ad26 100644
--- a/test/unit/exit_policy/rule.py
+++ b/test/unit/exit_policy/rule.py
@@ -6,6 +6,7 @@ import unittest
from stem.exit_policy import AddressType, ExitPolicyRule
+
class TestExitPolicyRule(unittest.TestCase):
def test_accept_or_reject(self):
self.assertTrue(ExitPolicyRule("accept *:*").is_accept)
diff --git a/test/unit/response/authchallenge.py b/test/unit/response/authchallenge.py
index 0583d04..314b953 100644
--- a/test/unit/response/authchallenge.py
+++ b/test/unit/response/authchallenge.py
@@ -20,6 +20,7 @@ INVALID_RESPONSE = "250 AUTHCHALLENGE \
SERVERHASH=FOOBARB16F72DACD4B5ED1531F3FCC04B593D46A1E30267E636EA7C7F8DD7A2B7BAA05 \
SERVERNONCE=FOOBAR653574272ABBB49395BD1060D642D653CFB7A2FCE6A4955BCFED819703A9998C"
+
class TestAuthChallengeResponse(unittest.TestCase):
def test_valid_response(self):
"""
diff --git a/test/unit/response/control_line.py b/test/unit/response/control_line.py
index 546a410..993afcb 100644
--- a/test/unit/response/control_line.py
+++ b/test/unit/response/control_line.py
@@ -14,6 +14,7 @@ PROTOCOLINFO_RESPONSE = (
'OK',
)
+
class TestControlLine(unittest.TestCase):
def test_pop_examples(self):
"""
diff --git a/test/unit/response/control_message.py b/test/unit/response/control_message.py
index 4a55c98..8c9ece7 100644
--- a/test/unit/response/control_message.py
+++ b/test/unit/response/control_message.py
@@ -30,6 +30,7 @@ version -- The current version of Tor.
250 OK
""".replace("\n", "\r\n")
+
class TestControlMessage(unittest.TestCase):
def test_ok_response(self):
"""
diff --git a/test/unit/response/events.py b/test/unit/response/events.py
index adc055c..4473c2a 100644
--- a/test/unit/response/events.py
+++ b/test/unit/response/events.py
@@ -308,6 +308,7 @@ def _get_event(content):
stem.response.convert("EVENT", controller_event, arrived_at = 25)
return controller_event
+
class TestEvents(unittest.TestCase):
def test_example(self):
"""
diff --git a/test/unit/response/getconf.py b/test/unit/response/getconf.py
index a072738..171fc8c 100644
--- a/test/unit/response/getconf.py
+++ b/test/unit/response/getconf.py
@@ -35,6 +35,7 @@ INVALID_RESPONSE = """\
123-FOO
232 BAR"""
+
class TestGetConfResponse(unittest.TestCase):
def test_empty_response(self):
"""
diff --git a/test/unit/response/getinfo.py b/test/unit/response/getinfo.py
index 589d7b9..2fa4f6e 100644
--- a/test/unit/response/getinfo.py
+++ b/test/unit/response/getinfo.py
@@ -49,6 +49,7 @@ DataDirectory /home/atagar/.tor
.
250 OK"""
+
class TestGetInfoResponse(unittest.TestCase):
def test_empty_response(self):
"""
diff --git a/test/unit/response/mapaddress.py b/test/unit/response/mapaddress.py
index c3e0b18..23faa6b 100644
--- a/test/unit/response/mapaddress.py
+++ b/test/unit/response/mapaddress.py
@@ -29,6 +29,7 @@ UNRECOGNIZED_KEYS_RESPONSE = "512 syntax error: mapping '2389' is not of expecte
FAILED_RESPONSE = "451 Resource exhausted"
+
class TestMapAddressResponse(unittest.TestCase):
def test_single_response(self):
"""
diff --git a/test/unit/response/protocolinfo.py b/test/unit/response/protocolinfo.py
index 28cd212..0a209f9 100644
--- a/test/unit/response/protocolinfo.py
+++ b/test/unit/response/protocolinfo.py
@@ -48,6 +48,7 @@ RELATIVE_COOKIE_PATH = r"""250-PROTOCOLINFO 1
250-VERSION Tor="0.2.1.30"
250 OK"""
+
class TestProtocolInfoResponse(unittest.TestCase):
def test_convert(self):
"""
diff --git a/test/unit/response/singleline.py b/test/unit/response/singleline.py
index e3b68e5..d48dee2 100644
--- a/test/unit/response/singleline.py
+++ b/test/unit/response/singleline.py
@@ -12,6 +12,7 @@ from test import mocking
MULTILINE_RESPONSE = """250-MULTI
250 LINE"""
+
class TestSingleLineResponse(unittest.TestCase):
def test_single_line_response(self):
message = mocking.get_message("552 NOTOK")
diff --git a/test/unit/tutorial.py b/test/unit/tutorial.py
index 47e8107..b0a1588 100644
--- a/test/unit/tutorial.py
+++ b/test/unit/tutorial.py
@@ -8,6 +8,7 @@ import unittest
from test import mocking
+
class TestTutorial(unittest.TestCase):
def tearDown(self):
mocking.revert_mocking()
diff --git a/test/unit/util/conf.py b/test/unit/util/conf.py
index 4e70c1d..5c4e094 100644
--- a/test/unit/util/conf.py
+++ b/test/unit/util/conf.py
@@ -9,6 +9,7 @@ import stem.util.enum
from stem.util.conf import parse_enum, parse_enum_csv
+
class TestConf(unittest.TestCase):
def tearDown(self):
# clears the config contents
diff --git a/test/unit/util/connection.py b/test/unit/util/connection.py
index 358ab49..5f4e62d 100644
--- a/test/unit/util/connection.py
+++ b/test/unit/util/connection.py
@@ -6,6 +6,7 @@ import unittest
import stem.util.connection
+
class TestConnection(unittest.TestCase):
def test_is_valid_ip_address(self):
"""
diff --git a/test/unit/util/enum.py b/test/unit/util/enum.py
index 1b33296..2eae439 100644
--- a/test/unit/util/enum.py
+++ b/test/unit/util/enum.py
@@ -6,6 +6,7 @@ import unittest
import stem.util.enum
+
class TestEnum(unittest.TestCase):
def test_enum_examples(self):
"""
diff --git a/test/unit/util/proc.py b/test/unit/util/proc.py
index 85aff2f..ded149f 100644
--- a/test/unit/util/proc.py
+++ b/test/unit/util/proc.py
@@ -9,6 +9,7 @@ import unittest
from stem.util import proc
from test import mocking
+
class TestProc(unittest.TestCase):
def tearDown(self):
mocking.revert_mocking()
diff --git a/test/unit/util/str_tools.py b/test/unit/util/str_tools.py
index c1c5ee8..6b10ade 100644
--- a/test/unit/util/str_tools.py
+++ b/test/unit/util/str_tools.py
@@ -7,6 +7,7 @@ import unittest
from stem.util import str_tools
+
class TestStrTools(unittest.TestCase):
def test_to_camel_case(self):
"""
diff --git a/test/unit/util/system.py b/test/unit/util/system.py
index e780375..2f1f6e1 100644
--- a/test/unit/util/system.py
+++ b/test/unit/util/system.py
@@ -56,6 +56,7 @@ GET_PID_BY_PORT_LSOF_RESULTS = [
"tor 1745 atagar 6u IPv4 14229 0t0 TCP 127.0.0.1:9051 (LISTEN)",
"apache 329 atagar 6u IPv4 14229 0t0 TCP 127.0.0.1:80 (LISTEN)"]
+
def mock_call(base_cmd, responses):
"""
Provides mocking for the system module's call function. There are a couple
@@ -93,6 +94,7 @@ def mock_call(base_cmd, responses):
return functools.partial(_mock_call, base_cmd, responses)
+
class TestSystem(unittest.TestCase):
def setUp(self):
mocking.mock(stem.util.proc.is_available, mocking.return_false())
diff --git a/test/unit/util/tor_tools.py b/test/unit/util/tor_tools.py
index 59f448b..634a57b 100644
--- a/test/unit/util/tor_tools.py
+++ b/test/unit/util/tor_tools.py
@@ -6,6 +6,7 @@ import unittest
import stem.util.tor_tools
+
class TestTorTools(unittest.TestCase):
def test_is_valid_fingerprint(self):
"""
diff --git a/test/unit/version.py b/test/unit/version.py
index 78a1a49..95aa690 100644
--- a/test/unit/version.py
+++ b/test/unit/version.py
@@ -15,6 +15,7 @@ TOR_VERSION_OUTPUT = """Mar 22 23:09:37.088 [notice] Tor v0.2.2.35 \
strong anonymity. (Running on Linux i686)
Tor version 0.2.2.35 (git-73ff13ab3cc9570d)."""
+
class TestVersion(unittest.TestCase):
def tearDown(self):
mocking.revert_mocking()
diff --git a/test/util.py b/test/util.py
index db6f53c..597dca5 100644
--- a/test/util.py
+++ b/test/util.py
@@ -18,6 +18,7 @@ Accept-Encoding: identity
"""
+
def external_ip(host, port):
"""
Returns the externally visible IP address when using a SOCKS4a proxy.
@@ -51,6 +52,7 @@ def external_ip(host, port):
except Exception, exc:
return None
+
def negotiate_socks(sock, host, port):
"""
Negotiate with a socks4a server. Closes the socket and raises an exception on
More information about the tor-commits
mailing list