[tor-commits] [stem/master] Unified the python2 and 3 codebase

atagar at torproject.org atagar at torproject.org
Sun Jan 4 02:29:03 UTC 2015


commit 9e0b460fc45f57f5c689cf3c8edbc68f92c18f9b
Author: Foxboron <mcfoxax at gmail.com>
Date:   Tue Dec 23 13:57:50 2014 +0100

    Unified the python2 and 3 codebase
    
    No longer needed to run a tool like 2to3.
    Added a file called _compat.py who is used to keep
    track of changes depending on versions.
    alias `unicode` to `str` on Py3
    alias `long` to `int` on Py3
---
 docs/conf.py                                       |   10 +-
 docs/republish.py                                  |    6 +-
 run_tests.py                                       |   14 +-
 stem/__init__.py                                   |    2 +-
 stem/_compat.py                                    |   16 +
 stem/connection.py                                 |   20 +-
 stem/control.py                                    |   68 ++--
 stem/descriptor/__init__.py                        |    1 +
 stem/descriptor/export.py                          |    8 +-
 stem/descriptor/extrainfo_descriptor.py            |   10 +-
 stem/descriptor/microdescriptor.py                 |    6 +-
 stem/descriptor/networkstatus.py                   |   38 +--
 stem/descriptor/reader.py                          |   19 +-
 stem/descriptor/remote.py                          |   16 +-
 stem/descriptor/router_status_entry.py             |   10 +-
 stem/descriptor/server_descriptor.py               |   12 +-
 stem/descriptor/tordnsel.py                        |    2 +-
 stem/exit_policy.py                                |   11 +-
 stem/interpreter/__init__.py                       |   18 +-
 stem/interpreter/arguments.py                      |    2 +-
 stem/interpreter/autocomplete.py                   |    2 +-
 stem/interpreter/commands.py                       |    6 +-
 stem/interpreter/help.py                           |    2 +-
 stem/process.py                                    |    2 +-
 stem/response/__init__.py                          |   10 +-
 stem/response/events.py                            |    3 +-
 stem/socket.py                                     |    1 +
 stem/util/conf.py                                  |    6 +-
 stem/util/connection.py                            |   11 +-
 stem/util/enum.py                                  |    4 +-
 stem/util/log.py                                   |    2 +-
 stem/util/ordereddict.py                           |    8 +-
 stem/util/str_tools.py                             |    3 +-
 stem/util/system.py                                |   15 +-
 stem/util/test_tools.py                            |    2 +-
 test/integ/connection/connect.py                   |   12 +-
 test/integ/control/base_controller.py              |   36 +-
 test/integ/control/controller.py                   |    8 +-
 test/integ/descriptor/remote.py                    |    2 +-
 test/integ/descriptor/server_descriptor.py         |    8 +-
 test/integ/process.py                              |    6 +-
 test/integ/socket/control_message.py               |   42 +--
 test/integ/socket/control_socket.py                |    4 +-
 test/integ/util/conf.py                            |   20 +-
 test/integ/util/proc.py                            |    6 +-
 test/integ/util/system.py                          |   52 +--
 test/integ/version.py                              |    2 +-
 test/mocking.py                                    |    8 +-
 test/output.py                                     |    4 +-
 test/runner.py                                     |    6 +-
 test/unit/connection/connect.py                    |   12 +-
 test/unit/control/controller.py                    |    2 +-
 test/unit/descriptor/export.py                     |   15 +-
 test/unit/descriptor/extrainfo_descriptor.py       |  228 ++++++-------
 test/unit/descriptor/microdescriptor.py            |   74 ++--
 .../descriptor/networkstatus/bridge_document.py    |   38 +--
 .../networkstatus/directory_authority.py           |    2 +-
 test/unit/descriptor/networkstatus/document_v2.py  |  118 +++----
 test/unit/descriptor/networkstatus/document_v3.py  |  360 ++++++++++----------
 .../descriptor/networkstatus/key_certificate.py    |   12 +-
 test/unit/descriptor/reader.py                     |   50 +--
 test/unit/descriptor/router_status_entry.py        |   46 +--
 test/unit/descriptor/server_descriptor.py          |  326 +++++++++---------
 test/unit/descriptor/tordnsel.py                   |    2 +-
 test/unit/exit_policy/policy.py                    |   44 +--
 test/unit/exit_policy/rule.py                      |   78 ++---
 test/unit/response/authchallenge.py                |    2 +-
 test/unit/response/control_line.py                 |   78 ++---
 test/unit/response/control_message.py              |   52 +--
 test/unit/response/events.py                       |    2 +-
 test/unit/response/protocolinfo.py                 |   40 +--
 test/unit/tutorial.py                              |   45 +--
 test/unit/tutorial_examples.py                     |   86 ++---
 test/unit/util/conf.py                             |   60 ++--
 test/unit/util/connection.py                       |   30 +-
 test/unit/util/enum.py                             |   20 +-
 test/unit/util/proc.py                             |   28 +-
 test/unit/util/str_tools.py                        |   82 ++---
 test/unit/util/system.py                           |   92 ++---
 test/unit/version.py                               |    4 +-
 test/util.py                                       |    4 +-
 81 files changed, 1341 insertions(+), 1273 deletions(-)

diff --git a/docs/conf.py b/docs/conf.py
index e778923..a12bfcf 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -51,8 +51,8 @@ if __version__.endswith('-dev'):
   __version__ = __version__[:-4]
 
 # General information about the project.
-project = u'Stem'
-copyright = u'2012, %s' % __author__
+project = 'Stem'
+copyright = '2012, %s' % __author__
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
@@ -192,8 +192,8 @@ htmlhelp_basename = 'Stemdoc'
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'Stem.tex', u'Stem Documentation',
-   u'Damian Johnson', 'manual'),
+  ('index', 'Stem.tex', 'Stem Documentation',
+   'Damian Johnson', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
@@ -225,7 +225,7 @@ latex_documents = [
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('index', 'stem', u'Stem Documentation',
+    ('index', 'stem', 'Stem Documentation',
      ['%s (%s)' % (__author__, __contact__)], 1)
 ]
 
diff --git a/docs/republish.py b/docs/republish.py
index ccb6234..8e168b6 100644
--- a/docs/republish.py
+++ b/docs/republish.py
@@ -56,7 +56,7 @@ if __name__ == '__main__':
   try:
     opts = getopt.getopt(sys.argv[1:], OPT, OPT_EXPANDED)[0]
   except getopt.GetoptError as exc:
-    print "%s (for usage provide --help)" % exc
+    print("%s (for usage provide --help)" % exc)
     sys.exit(1)
 
   repeat_rate = None
@@ -66,10 +66,10 @@ if __name__ == '__main__':
       if arg.isdigit():
         repeat_rate = int(arg)
       else:
-        print "The --repeat argument must be an integer, got '%s'" % arg
+        print("The --repeat argument must be an integer, got '%s'" % arg)
         sys.exit(1)
     elif opt in ("-h", "--help"):
-      print HELP_MSG
+      print(HELP_MSG)
       sys.exit()
 
   if repeat_rate:
diff --git a/run_tests.py b/run_tests.py
index 2a541cf..c881f66 100755
--- a/run_tests.py
+++ b/run_tests.py
@@ -9,12 +9,16 @@ Runs unit and integration tests. For usage information run this with '--help'.
 import collections
 import getopt
 import os
-import StringIO
 import sys
 import threading
 import time
 import unittest
 
+try:
+  from StringIO import StringIO
+except ImportError:
+  from io import StringIO
+
 import stem.prereq
 import stem.util.conf
 import stem.util.enum
@@ -296,14 +300,14 @@ def main():
     static_check_issues = {}
 
     if pyflakes_task and pyflakes_task.is_successful:
-      for path, issues in pyflakes_task.result.items():
+      for path, issues in list(pyflakes_task.result.items()):
         for issue in issues:
           static_check_issues.setdefault(path, []).append(issue)
     elif not stem.util.test_tools.is_pyflakes_available():
       println("Static error checking requires pyflakes version 0.7.3 or later. Please install it from ...\n  http://pypi.python.org/pypi/pyflakes\n", ERROR)
 
     if pep8_task and pep8_task.is_successful:
-      for path, issues in pep8_task.result.items():
+      for path, issues in list(pep8_task.result.items()):
         for issue in issues:
           static_check_issues.setdefault(path, []).append(issue)
     elif not stem.util.test_tools.is_pep8_available():
@@ -399,7 +403,7 @@ def _get_args(argv):
 
   # translates our args dict into a named tuple
 
-  Args = collections.namedtuple('Args', args.keys())
+  Args = collections.namedtuple('Args', list(args.keys()))
   return Args(**args)
 
 
@@ -446,7 +450,7 @@ def _run_test(args, test_class, output_filters, logging_buffer):
 
   suite = unittest.TestLoader().loadTestsFromTestCase(test_class)
 
-  test_results = StringIO.StringIO()
+  test_results = StringIO()
   run_result = unittest.TextTestRunner(test_results, verbosity=2).run(suite)
 
   if args.verbose:
diff --git a/stem/__init__.py b/stem/__init__.py
index baa2ef8..3ac4565 100644
--- a/stem/__init__.py
+++ b/stem/__init__.py
@@ -712,7 +712,7 @@ StreamStatus = stem.util.enum.UppercaseEnum(
 )
 
 # StreamClosureReason is a superset of RelayEndReason
-StreamClosureReason = stem.util.enum.UppercaseEnum(*(RelayEndReason.keys() + [
+StreamClosureReason = stem.util.enum.UppercaseEnum(*(list(RelayEndReason.keys()) + [
   'END',
   'PRIVATE_ADDR',
 ]))
diff --git a/stem/_compat.py b/stem/_compat.py
new file mode 100644
index 0000000..0fb3bfc
--- /dev/null
+++ b/stem/_compat.py
@@ -0,0 +1,16 @@
+import sys
+
+PY27 = sys.version_info >= (2, 7)
+PY3 = sys.version_info[0] >= 3
+PY33 = sys.version_info >= (3, 3)
+PY34 = sys.version_info >= (3, 4)
+
+if PY3:
+    unicode = str
+else:
+    unicode = unicode  # NOQA
+
+if PY3:
+    long = int
+else:
+    long = long  # NOQA
diff --git a/stem/connection.py b/stem/connection.py
index a38422b..609f683 100644
--- a/stem/connection.py
+++ b/stem/connection.py
@@ -271,7 +271,7 @@ def connect(control_port = ('127.0.0.1', 9051), control_socket = '/var/run/tor/c
       is_tor_running = stem.util.system.is_running('tor') or stem.util.system.is_running('tor.real')
       error_msg = CONNECT_MESSAGES['no_control_port'] if is_tor_running else CONNECT_MESSAGES['tor_isnt_running']
 
-    print error_msg
+    print(error_msg)
     return None
 
   return _connect_auth(control_connection, password, password_prompt, chroot_path, controller)
@@ -300,7 +300,7 @@ def connect_port(address = '127.0.0.1', port = 9051, password = None, chroot_pat
   try:
     control_port = stem.socket.ControlPort(address, port)
   except stem.SocketError as exc:
-    print exc
+    print(exc)
     return None
 
   return _connect_auth(control_port, password, True, chroot_path, controller)
@@ -330,7 +330,7 @@ def connect_socket_file(path = '/var/run/tor/control', password = None, chroot_p
   try:
     control_socket = stem.socket.ControlSocketFile(path)
   except stem.SocketError as exc:
-    print exc
+    print(exc)
     return None
 
   return _connect_auth(control_socket, password, True, chroot_path, controller)
@@ -361,18 +361,18 @@ def _connect_auth(control_socket, password, password_prompt, chroot_path, contro
       return controller(control_socket, is_authenticated = True)
   except IncorrectSocketType:
     if isinstance(control_socket, stem.socket.ControlPort):
-      print CONNECT_MESSAGES['wrong_port_type'].format(port = control_socket.get_port())
+      print(CONNECT_MESSAGES['wrong_port_type'].format(port = control_socket.get_port()))
     else:
-      print CONNECT_MESSAGES['wrong_socket_type']
+      print(CONNECT_MESSAGES['wrong_socket_type'])
 
     control_socket.close()
     return None
   except UnrecognizedAuthMethods as exc:
-    print CONNECT_MESSAGES['uncrcognized_auth_type'].format(auth_methods = ', '.join(exc.unknown_auth_methods))
+    print(CONNECT_MESSAGES['uncrcognized_auth_type'].format(auth_methods = ', '.join(exc.unknown_auth_methods)))
     control_socket.close()
     return None
   except IncorrectPassword:
-    print CONNECT_MESSAGES['incorrect_password']
+    print(CONNECT_MESSAGES['incorrect_password'])
     control_socket.close()
     return None
   except MissingPassword:
@@ -389,15 +389,15 @@ def _connect_auth(control_socket, password, password_prompt, chroot_path, contro
 
       return _connect_auth(control_socket, password, password_prompt, chroot_path, controller)
     else:
-      print CONNECT_MESSAGES['needs_password']
+      print(CONNECT_MESSAGES['needs_password'])
       control_socket.close()
       return None
   except UnreadableCookieFile as exc:
-    print CONNECT_MESSAGES['unreadable_cookie_file'].format(path = exc.cookie_path, issue = str(exc))
+    print(CONNECT_MESSAGES['unreadable_cookie_file'].format(path = exc.cookie_path, issue = str(exc)))
     control_socket.close()
     return None
   except AuthenticationFailure as exc:
-    print CONNECT_MESSAGES['general_auth_failure'].format(error = exc)
+    print(CONNECT_MESSAGES['general_auth_failure'].format(error = exc))
     control_socket.close()
     return None
 
diff --git a/stem/control.py b/stem/control.py
index 4d45b8d..8986c21 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -225,8 +225,6 @@ import functools
 import inspect
 import io
 import os
-import Queue
-import StringIO
 import threading
 import time
 
@@ -236,6 +234,13 @@ try:
 except ImportError:
   from stem.util.ordereddict import OrderedDict
 
+try:
+  import queue
+  from io import StringIO
+except ImportError:
+  import Queue as queue
+  from StringIO import StringIO
+
 import stem.descriptor.microdescriptor
 import stem.descriptor.reader
 import stem.descriptor.router_status_entry
@@ -253,6 +258,7 @@ import stem.version
 
 from stem import UNDEFINED, CircStatus, Signal
 from stem.util import log
+from stem._compat import unicode
 
 # state changes a control socket can have
 
@@ -437,8 +443,8 @@ class BaseController(object):
     self._status_listeners_lock = threading.RLock()
 
     # queues where incoming messages are directed
-    self._reply_queue = Queue.Queue()
-    self._event_queue = Queue.Queue()
+    self._reply_queue = queue.Queue()
+    self._event_queue = queue.Queue()
 
     # thread to continually pull from the control socket
     self._reader_thread = None
@@ -522,7 +528,7 @@ class BaseController(object):
             log.info('Socket experienced a problem (%s)' % response)
           elif isinstance(response, stem.response.ControlMessage):
             log.info('Failed to deliver a response: %s' % response)
-        except Queue.Empty:
+        except queue.Empty:
           # the empty() method is documented to not be fully reliable so this
           # isn't entirely surprising
 
@@ -840,7 +846,7 @@ class BaseController(object):
       try:
         event_message = self._event_queue.get_nowait()
         self._handle_event(event_message)
-      except Queue.Empty:
+      except queue.Empty:
         if not self.is_alive():
           break
 
@@ -920,7 +926,7 @@ class Controller(BaseController):
       if self.is_caching_enabled():
         self._set_cache(dict((k, None) for k in event.config), 'getconf')
 
-        if 'exitpolicy' in event.config.keys():
+        if 'exitpolicy' in list(event.config.keys()):
           self._set_cache({'exitpolicy': None})
 
     self.add_event_listener(_confchanged_listener, EventType.CONF_CHANGED)
@@ -1014,12 +1020,12 @@ class Controller(BaseController):
 
     # if everything was cached then short circuit making the query
     if not params:
-      log.trace('GETINFO %s (cache fetch)' % ' '.join(reply.keys()))
+      log.trace('GETINFO %s (cache fetch)' % ' '.join(list(reply.keys())))
 
       if is_multiple:
         return reply
       else:
-        return reply.values()[0]
+        return list(reply.values())[0]
 
     try:
       response = self.msg('GETINFO %s' % ' '.join(params))
@@ -1029,14 +1035,14 @@ class Controller(BaseController):
       # usually we want unicode values under python 3.x
 
       if stem.prereq.is_python_3() and not get_bytes:
-        response.entries = dict((k, stem.util.str_tools._to_unicode(v)) for (k, v) in response.entries.items())
+        response.entries = dict((k, stem.util.str_tools._to_unicode(v)) for (k, v) in list(response.entries.items()))
 
       reply.update(response.entries)
 
       if self.is_caching_enabled():
         to_cache = {}
 
-        for key, value in response.entries.items():
+        for key, value in list(response.entries.items()):
           key = key.lower()  # make case insensitive
 
           if key in CACHEABLE_GETINFO_PARAMS:
@@ -1053,7 +1059,7 @@ class Controller(BaseController):
       if is_multiple:
         return reply
       else:
-        return reply.values()[0]
+        return list(reply.values())[0]
     except stem.ControllerError as exc:
       # bump geoip failure count if...
       # * we're caching results
@@ -1885,7 +1891,7 @@ class Controller(BaseController):
       params = [params]
 
     # remove strings which contain only whitespace
-    params = filter(lambda entry: entry.strip(), params)
+    params = [entry for entry in params if entry.strip()]
 
     if params == []:
       return {}
@@ -1905,7 +1911,7 @@ class Controller(BaseController):
 
     # if everything was cached then short circuit making the query
     if not lookup_params:
-      log.trace('GETCONF %s (cache fetch)' % ' '.join(reply.keys()))
+      log.trace('GETCONF %s (cache fetch)' % ' '.join(list(reply.keys())))
       return self._get_conf_dict_to_response(reply, default, multiple)
 
     try:
@@ -1914,7 +1920,7 @@ class Controller(BaseController):
       reply.update(response.entries)
 
       if self.is_caching_enabled():
-        to_cache = dict((k.lower(), v) for k, v in response.entries.items())
+        to_cache = dict((k.lower(), v) for k, v in list(response.entries.items()))
 
         for key in UNCACHEABLE_GETCONF_PARAMS:
           if key in to_cache:
@@ -1933,7 +1939,7 @@ class Controller(BaseController):
       # be sure what they wanted.
 
       for key in reply:
-        if not key.lower() in MAPPED_CONFIG_KEYS.values():
+        if not key.lower() in list(MAPPED_CONFIG_KEYS.values()):
           user_expected_key = _case_insensitive_lookup(params, key, key)
 
           if key != user_expected_key:
@@ -1959,7 +1965,7 @@ class Controller(BaseController):
 
     return_dict = {}
 
-    for key, values in config_dict.items():
+    for key, values in list(config_dict.items()):
       if values == []:
         # config option was unset
         if default != UNDEFINED:
@@ -2048,7 +2054,7 @@ class Controller(BaseController):
     query_comp = ['RESETCONF' if reset else 'SETCONF']
 
     if isinstance(params, dict):
-      params = params.items()
+      params = list(params.items())
 
     for param, value in params:
       if isinstance(value, str):
@@ -2228,7 +2234,7 @@ class Controller(BaseController):
     for directory in conf:
       hidden_service_options.append(('HiddenServiceDir', directory))
 
-      for k, v in conf[directory].items():
+      for k, v in list(conf[directory].items()):
         if k == 'HiddenServicePort':
           for entry in v:
             if isinstance(entry, int):
@@ -2432,7 +2438,7 @@ class Controller(BaseController):
     with self._event_listeners_lock:
       event_types_changed = False
 
-      for event_type, event_listeners in self._event_listeners.items():
+      for event_type, event_listeners in list(self._event_listeners.items()):
         if listener in event_listeners:
           event_listeners.remove(listener)
 
@@ -2441,7 +2447,7 @@ class Controller(BaseController):
             del self._event_listeners[event_type]
 
       if event_types_changed:
-        response = self.msg('SETEVENTS %s' % ' '.join(self._event_listeners.keys()))
+        response = self.msg('SETEVENTS %s' % ' '.join(list(self._event_listeners.keys())))
 
         if not response.is_ok():
           raise stem.ProtocolError('SETEVENTS received unexpected response\n%s' % response)
@@ -2496,7 +2502,7 @@ class Controller(BaseController):
       if not self.is_caching_enabled():
         return
 
-      for key, value in params.items():
+      for key, value in list(params.items()):
         if namespace:
           cache_key = '%s.%s' % (namespace, key)
         else:
@@ -2691,7 +2697,7 @@ class Controller(BaseController):
     response = self.get_info('circuit-status')
 
     for circ in response.splitlines():
-      circ_message = stem.socket.recv_message(StringIO.StringIO('650 CIRC ' + circ + '\r\n'))
+      circ_message = stem.socket.recv_message(StringIO('650 CIRC ' + circ + '\r\n'))
       stem.response.convert('EVENT', circ_message, arrived_at = 0)
       circuits.append(circ_message)
 
@@ -2755,7 +2761,7 @@ class Controller(BaseController):
     circ_queue, circ_listener = None, None
 
     if await_build:
-      circ_queue = Queue.Queue()
+      circ_queue = queue.Queue()
 
       def circ_listener(event):
         circ_queue.put(event)
@@ -2876,7 +2882,7 @@ class Controller(BaseController):
     response = self.get_info('stream-status')
 
     for stream in response.splitlines():
-      message = stem.socket.recv_message(StringIO.StringIO('650 STREAM ' + stream + '\r\n'))
+      message = stem.socket.recv_message(StringIO('650 STREAM ' + stream + '\r\n'))
       stem.response.convert('EVENT', message, arrived_at = 0)
       streams.append(message)
 
@@ -3070,7 +3076,7 @@ class Controller(BaseController):
     :returns: **dict** with 'original -> replacement' address mappings
     """
 
-    mapaddress_arg = ' '.join(['%s=%s' % (k, v) for (k, v) in mapping.items()])
+    mapaddress_arg = ' '.join(['%s=%s' % (k, v) for (k, v) in list(mapping.items())])
     response = self.msg('MAPADDRESS %s' % mapaddress_arg)
     stem.response.convert('MAPADDRESS', response)
 
@@ -3132,7 +3138,7 @@ class Controller(BaseController):
     stem.response.convert('EVENT', event_message, arrived_at = time.time())
 
     with self._event_listeners_lock:
-      for event_type, event_listeners in self._event_listeners.items():
+      for event_type, event_listeners in list(self._event_listeners.items()):
         if event_type == event_message.type:
           for listener in event_listeners:
             listener(event_message)
@@ -3152,10 +3158,10 @@ class Controller(BaseController):
     with self._event_listeners_lock:
       if self.is_authenticated():
         # try to set them all
-        response = self.msg('SETEVENTS %s' % ' '.join(self._event_listeners.keys()))
+        response = self.msg('SETEVENTS %s' % ' '.join(list(self._event_listeners.keys())))
 
         if response.is_ok():
-          set_events = self._event_listeners.keys()
+          set_events = list(self._event_listeners.keys())
         else:
           # One of the following likely happened...
           #
@@ -3170,7 +3176,7 @@ class Controller(BaseController):
           #
           # See if we can set some subset of our events.
 
-          for event in self._event_listeners.keys():
+          for event in list(self._event_listeners.keys()):
             response = self.msg('SETEVENTS %s' % ' '.join(set_events + [event]))
 
             if response.is_ok():
@@ -3278,7 +3284,7 @@ def _case_insensitive_lookup(entries, key, default = UNDEFINED):
 
   if entries is not None:
     if isinstance(entries, dict):
-      for k, v in entries.items():
+      for k, v in list(entries.items()):
         if k.lower() == key.lower():
           return v
     else:
diff --git a/stem/descriptor/__init__.py b/stem/descriptor/__init__.py
index 1527338..25a880e 100644
--- a/stem/descriptor/__init__.py
+++ b/stem/descriptor/__init__.py
@@ -58,6 +58,7 @@ import stem.prereq
 import stem.util.enum
 import stem.util.str_tools
 import stem.util.system
+from stem._compat import unicode
 
 try:
   # added in python 2.7
diff --git a/stem/descriptor/export.py b/stem/descriptor/export.py
index fce6eb0..07ef467 100644
--- a/stem/descriptor/export.py
+++ b/stem/descriptor/export.py
@@ -12,9 +12,13 @@ Toolkit for exporting descriptors to other formats.
   export_csv_file - Writes exported CSV output to a file
 """
 
-import cStringIO
 import csv
 
+try:
+  from cStringIO import StringIO
+except ImportError:
+  from io import StringIO
+
 import stem.descriptor
 import stem.prereq
 
@@ -41,7 +45,7 @@ def export_csv(descriptors, included_fields = (), excluded_fields = (), header =
   :raises: **ValueError** if descriptors contain more than one descriptor type
   """
 
-  output_buffer = cStringIO.StringIO()
+  output_buffer = StringIO()
   export_csv_file(output_buffer, descriptors, included_fields, excluded_fields, header)
   return output_buffer.getvalue()
 
diff --git a/stem/descriptor/extrainfo_descriptor.py b/stem/descriptor/extrainfo_descriptor.py
index b260888..d6e6102 100644
--- a/stem/descriptor/extrainfo_descriptor.py
+++ b/stem/descriptor/extrainfo_descriptor.py
@@ -437,11 +437,11 @@ class ExtraInfoDescriptor(Descriptor):
           raise ValueError("The '%s' entry can only appear once in an extra-info descriptor" % keyword)
 
       expected_first_keyword = self._first_keyword()
-      if expected_first_keyword and expected_first_keyword != entries.keys()[0]:
+      if expected_first_keyword and expected_first_keyword != list(entries.keys())[0]:
         raise ValueError("Extra-info descriptor must start with a '%s' entry" % expected_first_keyword)
 
       expected_last_keyword = self._last_keyword()
-      if expected_last_keyword and expected_last_keyword != entries.keys()[-1]:
+      if expected_last_keyword and expected_last_keyword != list(entries.keys())[-1]:
         raise ValueError("Descriptor must end with a '%s' entry" % expected_last_keyword)
 
     self._parse(entries, validate)
@@ -460,7 +460,7 @@ class ExtraInfoDescriptor(Descriptor):
     :raises: **ValueError** if an error occurs in validation
     """
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       # most just work with the first (and only) value
       value, _, _ = values[0]
       line = '%s %s' % (keyword, value)  # original line
@@ -884,7 +884,7 @@ class RelayExtraInfoDescriptor(ExtraInfoDescriptor):
     entries = dict(entries)  # shallow copy since we're destructive
 
     # handles fields only in server descriptors
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, block_type, block_contents = values[0]
 
       line = '%s %s' % (keyword, value)  # original line
@@ -920,7 +920,7 @@ class BridgeExtraInfoDescriptor(ExtraInfoDescriptor):
     entries = dict(entries)  # shallow copy since we're destructive
 
     # handles fields only in server descriptors
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, _, _ = values[0]
       line = '%s %s' % (keyword, value)  # original line
 
diff --git a/stem/descriptor/microdescriptor.py b/stem/descriptor/microdescriptor.py
index 4702b84..e5d690f 100644
--- a/stem/descriptor/microdescriptor.py
+++ b/stem/descriptor/microdescriptor.py
@@ -142,7 +142,7 @@ def _parse_file(descriptor_file, validate = True, **kwargs):
         descriptor_lines = descriptor_lines[1:]
 
       # strip newlines from annotations
-      annotations = map(bytes.strip, annotations)
+      annotations = list(map(bytes.strip, annotations))
 
       descriptor_text = bytes.join(b'', descriptor_lines)
 
@@ -248,7 +248,7 @@ class Microdescriptor(Descriptor):
     :raises: **ValueError** if an error occurs in validation
     """
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       # most just work with the first (and only) value
       value, block_type, block_contents = values[0]
 
@@ -302,7 +302,7 @@ class Microdescriptor(Descriptor):
       if keyword in entries and len(entries[keyword]) > 1:
         raise ValueError("The '%s' entry can only appear once in a microdescriptor" % keyword)
 
-    if "onion-key" != entries.keys()[0]:
+    if "onion-key" != list(entries.keys())[0]:
       raise ValueError("Microdescriptor must start with a 'onion-key' entry")
 
   def _name(self, is_plural = False):
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
index 6a15caf..65bf5cc 100644
--- a/stem/descriptor/networkstatus.py
+++ b/stem/descriptor/networkstatus.py
@@ -351,7 +351,7 @@ class NetworkStatusDocumentV2(NetworkStatusDocument):
     self._parse(entries, validate)
 
   def _parse(self, entries, validate):
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, block_type, block_contents = values[0]
 
       line = '%s %s' % (keyword, value)  # original line
@@ -450,7 +450,7 @@ class NetworkStatusDocumentV2(NetworkStatusDocument):
       if keyword in entries and len(entries[keyword]) > 1:
         raise ValueError("Network status document (v2) can only have a single '%s' line, got %i:\n%s" % (keyword, len(entries[keyword]), str(self)))
 
-    if 'network-status-version' != entries.keys()[0]:
+    if 'network-status-version' != list(entries.keys())[0]:
       raise ValueError("Network status document (v2) are expected to start with a 'network-status-version' line:\n%s" % str(self))
 
 
@@ -516,7 +516,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument):
     self._header = _DocumentHeader(document_file, validate, default_params)
 
     # merge header attributes into us
-    for attr, value in vars(self._header).items():
+    for attr, value in list(vars(self._header).items()):
       if attr != '_unrecognized_lines':
         setattr(self, attr, value)
       else:
@@ -553,7 +553,7 @@ class NetworkStatusDocumentV3(NetworkStatusDocument):
     self._footer = _DocumentFooter(document_file, validate, self._header)
 
     # merge header attributes into us
-    for attr, value in vars(self._footer).items():
+    for attr, value in list(vars(self._footer).items()):
       if attr != '_unrecognized_lines':
         setattr(self, attr, value)
       else:
@@ -630,12 +630,12 @@ class _DocumentHeader(object):
     if self.consensus_method is not None:
       return self.consensus_method >= method
     elif self.consensus_methods is not None:
-      return bool(filter(lambda x: x >= method, self.consensus_methods))
+      return bool([x for x in self.consensus_methods if x >= method])
     else:
       return False  # malformed document
 
   def _parse(self, entries, validate):
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, _, _ = values[0]
       line = '%s %s' % (keyword, value)
 
@@ -795,7 +795,7 @@ class _DocumentHeader(object):
     Checks that the params we know about are within their documented ranges.
     """
 
-    for key, value in self.params.items():
+    for key, value in list(self.params.items()):
       # all parameters are constrained to int32 range
       minimum, maximum = -2147483648, 2147483647
 
@@ -874,17 +874,17 @@ class _DocumentFooter(object):
       # 'directory-footer'.
 
       if header.meets_consensus_method(9):
-        if entries.keys()[0] != 'directory-footer':
+        if list(entries.keys())[0] != 'directory-footer':
           raise ValueError("Network status document's footer should start with a 'directory-footer' line in consensus-method 9 or later")
       else:
-        if entries.keys()[0] != 'directory-signature':
+        if list(entries.keys())[0] != 'directory-signature':
           raise ValueError("Network status document's footer should start with a 'directory-signature' line prior to consensus-method 9")
 
       _check_for_missing_and_disallowed_fields(header, entries, FOOTER_STATUS_DOCUMENT_FIELDS)
       _check_for_misordered_fields(entries, FOOTER_FIELDS)
 
   def _parse(self, entries, validate, header):
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, block_type, block_contents = values[0]
       line = '%s %s' % (keyword, value)
 
@@ -941,11 +941,11 @@ def _check_for_missing_and_disallowed_fields(header, entries, fields):
   for field, in_votes, in_consensus, mandatory in fields:
     if mandatory and ((header.is_consensus and in_consensus) or (header.is_vote and in_votes)):
       # mandatory field, check that we have it
-      if field not in entries.keys():
+      if field not in list(entries.keys()):
         missing_fields.append(field)
     elif (header.is_consensus and not in_consensus) or (header.is_vote and not in_votes):
       # field we shouldn't have, check that we don't
-      if field in entries.keys():
+      if field in list(entries.keys()):
         disallowed_fields.append(field)
 
   if missing_fields:
@@ -972,12 +972,12 @@ def _check_for_misordered_fields(entries, expected):
   # document type or are unknown. Remove the unknown fields since they
   # reflect a spec change and can appear anywhere in the document.
 
-  actual = filter(lambda field: field in expected, entries.keys())
+  actual = [field for field in list(entries.keys()) if field in expected]
 
   # Narrow the expected to just what we have. If the lists then match then the
   # order's valid.
 
-  expected = filter(lambda field: field in actual, expected)
+  expected = [field for field in expected if field in actual]
 
   if actual != expected:
     actual_label = ', '.join(actual)
@@ -1112,7 +1112,7 @@ class DirectoryAuthority(Descriptor):
 
     entries = _get_descriptor_components(content, validate)
 
-    if validate and 'dir-source' != entries.keys()[0]:
+    if validate and 'dir-source' != list(entries.keys())[0]:
       raise ValueError("Authority entries are expected to start with a 'dir-source' line:\n%s" % (content))
 
     # check that we have mandatory fields
@@ -1151,7 +1151,7 @@ class DirectoryAuthority(Descriptor):
           type_label = 'votes' if is_vote else 'consensus entries'
           raise ValueError("Authority %s shouldn't have a '%s' line:\n%s" % (type_label, keyword, content))
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, _, _ = values[0]
       line = '%s %s' % (keyword, value)
 
@@ -1295,9 +1295,9 @@ class KeyCertificate(Descriptor):
     entries = _get_descriptor_components(content, validate)
 
     if validate:
-      if 'dir-key-certificate-version' != entries.keys()[0]:
+      if 'dir-key-certificate-version' != list(entries.keys())[0]:
         raise ValueError("Key certificates must start with a 'dir-key-certificate-version' line:\n%s" % (content))
-      elif 'dir-key-certification' != entries.keys()[-1]:
+      elif 'dir-key-certification' != list(entries.keys())[-1]:
         raise ValueError("Key certificates must end with a 'dir-key-certification' line:\n%s" % (content))
 
       # check that we have mandatory fields and that our known fields only
@@ -1311,7 +1311,7 @@ class KeyCertificate(Descriptor):
         if entry_count > 1:
           raise ValueError("Key certificates can only have a single '%s' line, got %i:\n%s" % (keyword, entry_count, content))
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, block_type, block_contents = values[0]
       line = '%s %s' % (keyword, value)
 
diff --git a/stem/descriptor/reader.py b/stem/descriptor/reader.py
index 1eecbee..bbd6c6c 100644
--- a/stem/descriptor/reader.py
+++ b/stem/descriptor/reader.py
@@ -79,13 +79,18 @@ and picks up where it left off if run again...
 
 import mimetypes
 import os
-import Queue
 import tarfile
 import threading
 
+try:
+  import queue
+except ImportError:
+  import Queue as queue
+
 import stem.descriptor
 import stem.prereq
 import stem.util.system
+from stem._compat import unicode
 
 # flag to indicate when the reader thread is out of descriptor files to read
 FINISHED = 'DONE'
@@ -221,7 +226,7 @@ def save_processed_files(path, processed_files):
     raise IOError(exc)
 
   with open(path, 'w') as output_file:
-    for path, timestamp in processed_files.items():
+    for path, timestamp in list(processed_files.items()):
       if not os.path.isabs(path):
         raise TypeError('Only absolute paths are acceptable: %s' % path)
 
@@ -265,7 +270,7 @@ class DescriptorReader(object):
 
     # expand any relative paths we got
 
-    target = map(os.path.abspath, target)
+    target = list(map(os.path.abspath, target))
 
     self._validate = validate
     self._follow_links = follow_links
@@ -288,7 +293,7 @@ class DescriptorReader(object):
     # Descriptors that we have read but not yet provided to the caller. A
     # FINISHED entry is used by the reading thread to indicate the end.
 
-    self._unreturned_descriptors = Queue.Queue(buffer_size)
+    self._unreturned_descriptors = queue.Queue(buffer_size)
 
     if self._persistence_path:
       try:
@@ -316,7 +321,7 @@ class DescriptorReader(object):
     """
 
     # make sure that we only provide back absolute paths
-    return dict((os.path.abspath(k), v) for (k, v) in self._processed_files.items())
+    return dict((os.path.abspath(k), v) for (k, v) in list(self._processed_files.items()))
 
   def set_processed_files(self, processed_files):
     """
@@ -400,7 +405,7 @@ class DescriptorReader(object):
       try:
         while True:
           self._unreturned_descriptors.get_nowait()
-      except Queue.Empty:
+      except queue.Empty:
         pass
 
       self._reader_thread.join()
@@ -447,7 +452,7 @@ class DescriptorReader(object):
             break
           else:
             yield descriptor
-        except Queue.Empty:
+        except queue.Empty:
           self._iter_notice.wait()
           self._iter_notice.clear()
 
diff --git a/stem/descriptor/remote.py b/stem/descriptor/remote.py
index d70c059..fa0a672 100644
--- a/stem/descriptor/remote.py
+++ b/stem/descriptor/remote.py
@@ -77,9 +77,13 @@ import random
 import sys
 import threading
 import time
-import urllib2
 import zlib
 
+try:
+    import urllib.request as urllib
+except ImportError:
+    import urllib2 as urllib
+
 import stem.descriptor
 
 from stem import Flag
@@ -340,7 +344,7 @@ class Query(object):
     """
 
     if use_authority or not self.endpoints:
-      authority = random.choice(filter(HAS_V3IDENT, get_authorities().values()))
+      authority = random.choice(list(filter(HAS_V3IDENT, list(get_authorities().values()))))
       address, dirport = authority.address, authority.dir_port
     else:
       address, dirport = random.choice(self.endpoints)
@@ -353,7 +357,7 @@ class Query(object):
       self.download_url = self._pick_url(use_authority)
 
       self.start_time = time.time()
-      response = urllib2.urlopen(self.download_url, timeout = self.timeout).read()
+      response = urllib.urlopen(self.download_url, timeout = self.timeout).read()
 
       if self.download_url.endswith('.z'):
         response = zlib.decompress(response)
@@ -390,7 +394,7 @@ class DescriptorDownloader(object):
   def __init__(self, use_mirrors = False, **default_args):
     self._default_args = default_args
 
-    authorities = filter(HAS_V3IDENT, get_authorities().values())
+    authorities = list(filter(HAS_V3IDENT, list(get_authorities().values())))
     self._endpoints = [(auth.address, auth.dir_port) for auth in authorities]
 
     if use_mirrors:
@@ -412,12 +416,12 @@ class DescriptorDownloader(object):
     :raises: **Exception** if unable to determine the directory mirrors
     """
 
-    authorities = filter(HAS_V3IDENT, get_authorities().values())
+    authorities = list(filter(HAS_V3IDENT, list(get_authorities().values())))
     new_endpoints = set([(auth.address, auth.dir_port) for auth in authorities])
 
     consensus = list(self.get_consensus(document_handler = stem.descriptor.DocumentHandler.DOCUMENT).run())[0]
 
-    for desc in consensus.routers.values():
+    for desc in list(consensus.routers.values()):
       if Flag.V2DIR in desc.flags:
         new_endpoints.add((desc.address, desc.dir_port))
 
diff --git a/stem/descriptor/router_status_entry.py b/stem/descriptor/router_status_entry.py
index 90d704c..ab93ada 100644
--- a/stem/descriptor/router_status_entry.py
+++ b/stem/descriptor/router_status_entry.py
@@ -170,7 +170,7 @@ class RouterStatusEntry(Descriptor):
     :raises: **ValueError** if a validity check fails
     """
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, _, _ = values[0]
 
       if keyword == 's':
@@ -198,7 +198,7 @@ class RouterStatusEntry(Descriptor):
       if keyword in entries and len(entries[keyword]) > 1:
         raise ValueError("%s can only have a single '%s' line, got %i:\n%s" % (self._name(True), keyword, len(entries[keyword]), str(self)))
 
-    if 'r' != entries.keys()[0]:
+    if 'r' != list(entries.keys())[0]:
       raise ValueError("%s are expected to start with a 'r' line:\n%s" % (self._name(True), str(self)))
 
   def _name(self, is_plural = False):
@@ -266,7 +266,7 @@ class RouterStatusEntryV2(RouterStatusEntry):
     super(RouterStatusEntryV2, self).__init__(content, validate, document)
 
   def _parse(self, entries, validate):
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, _, _ = values[0]
 
       if keyword == 'r':
@@ -346,7 +346,7 @@ class RouterStatusEntryV3(RouterStatusEntry):
     super(RouterStatusEntryV3, self).__init__(content, validate, document)
 
   def _parse(self, entries, validate):
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, _, _ = values[0]
 
       if keyword == 'r':
@@ -428,7 +428,7 @@ class RouterStatusEntryMicroV3(RouterStatusEntry):
     super(RouterStatusEntryMicroV3, self).__init__(content, validate, document)
 
   def _parse(self, entries, validate):
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, _, _ = values[0]
 
       if keyword == 'r':
diff --git a/stem/descriptor/server_descriptor.py b/stem/descriptor/server_descriptor.py
index 40c6a27..11820e3 100644
--- a/stem/descriptor/server_descriptor.py
+++ b/stem/descriptor/server_descriptor.py
@@ -150,7 +150,7 @@ def _parse_file(descriptor_file, is_bridge = False, validate = True, **kwargs):
         descriptor_content = descriptor_content[1:]
 
       # strip newlines from annotations
-      annotations = map(bytes.strip, annotations)
+      annotations = list(map(bytes.strip, annotations))
 
       descriptor_text = bytes.join(b'', descriptor_content)
 
@@ -368,7 +368,7 @@ class ServerDescriptor(Descriptor):
     :raises: **ValueError** if an error occurs in validation
     """
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       # most just work with the first (and only) value
       value, block_type, block_contents = values[0]
 
@@ -620,11 +620,11 @@ class ServerDescriptor(Descriptor):
         raise ValueError("The '%s' entry can only appear once in a descriptor" % keyword)
 
     expected_first_keyword = self._first_keyword()
-    if expected_first_keyword and expected_first_keyword != entries.keys()[0]:
+    if expected_first_keyword and expected_first_keyword != list(entries.keys())[0]:
       raise ValueError("Descriptor must start with a '%s' entry" % expected_first_keyword)
 
     expected_last_keyword = self._last_keyword()
-    if expected_last_keyword and expected_last_keyword != entries.keys()[-1]:
+    if expected_last_keyword and expected_last_keyword != list(entries.keys())[-1]:
       raise ValueError("Descriptor must end with a '%s' entry" % expected_last_keyword)
 
     if not self.exit_policy:
@@ -793,7 +793,7 @@ class RelayDescriptor(ServerDescriptor):
 
     # handles fields only in server descriptors
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, block_type, block_contents = values[0]
       line = '%s %s' % (keyword, value)
 
@@ -872,7 +872,7 @@ class BridgeDescriptor(ServerDescriptor):
     entries = dict(entries)
 
     # handles fields only in bridge descriptors
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, block_type, block_contents = values[0]
       line = '%s %s' % (keyword, value)
 
diff --git a/stem/descriptor/tordnsel.py b/stem/descriptor/tordnsel.py
index 951b72f..0a68580 100644
--- a/stem/descriptor/tordnsel.py
+++ b/stem/descriptor/tordnsel.py
@@ -74,7 +74,7 @@ class TorDNSEL(Descriptor):
 
   def _parse(self, entries, validate):
 
-    for keyword, values in entries.items():
+    for keyword, values in list(entries.items()):
       value, block_type, block_content = values[0]
 
       if validate and block_content:
diff --git a/stem/exit_policy.py b/stem/exit_policy.py
index 9bc8ccb..7ac3b8b 100644
--- a/stem/exit_policy.py
+++ b/stem/exit_policy.py
@@ -75,6 +75,8 @@ import stem.util.connection
 import stem.util.enum
 import stem.util.str_tools
 
+from stem._compat import unicode
+
 try:
   # added in python 3.2
   from functools import lru_cache
@@ -204,7 +206,6 @@ def _flag_private_rules(rules):
 
     if last_rule.is_address_wildcard() or last_rule.min_port != min_port or last_rule.max_port != max_port or last_rule.is_accept != is_accept:
       is_match = False
-
     if is_match:
       for rule in rule_set:
         rule._is_private = True
@@ -298,14 +299,14 @@ class ExitPolicy(object):
 
     for rule in self._get_rules():
       if rule.is_accept:
-        for port in xrange(rule.min_port, rule.max_port + 1):
+        for port in range(rule.min_port, rule.max_port + 1):
           if port not in rejected_ports:
             return True
       elif rule.is_address_wildcard():
         if rule.is_port_wildcard():
           return False
         else:
-          rejected_ports.update(range(rule.min_port, rule.max_port + 1))
+          rejected_ports.update(list(range(rule.min_port, rule.max_port + 1)))
 
     return self._is_allowed_default
 
@@ -351,7 +352,7 @@ class ExitPolicy(object):
       elif rule.is_port_wildcard():
         break
 
-      for port in xrange(rule.min_port, rule.max_port + 1):
+      for port in range(rule.min_port, rule.max_port + 1):
         if port in skip_ports:
           continue
 
@@ -1025,7 +1026,7 @@ def _address_type_to_int(address_type):
 
 
 def _int_to_address_type(address_type_int):
-  return AddressType[AddressType.keys()[address_type_int]]
+  return AddressType[list(AddressType.keys())[address_type_int]]
 
 
 class MicroExitPolicyRule(ExitPolicyRule):
diff --git a/stem/interpreter/__init__.py b/stem/interpreter/__init__.py
index a296163..f4fac8e 100644
--- a/stem/interpreter/__init__.py
+++ b/stem/interpreter/__init__.py
@@ -52,11 +52,11 @@ def main():
   try:
     args = stem.interpreter.arguments.parse(sys.argv[1:])
   except ValueError as exc:
-    print exc
+    print(exc)
     sys.exit(1)
 
   if args.print_help:
-    print stem.interpreter.arguments.get_help()
+    print(stem.interpreter.arguments.get_help())
     sys.exit()
 
   if args.disable_color:
@@ -72,10 +72,10 @@ def main():
 
     if not is_tor_running:
       if not stem.util.system.is_available('tor'):
-        print format(msg('msg.tor_unavailable'), *ERROR_OUTPUT)
+        print(format(msg('msg.tor_unavailable'), *ERROR_OUTPUT))
         sys.exit(1)
       else:
-        print format(msg('msg.starting_tor'), *HEADER_OUTPUT)
+        print(format(msg('msg.starting_tor'), *HEADER_OUTPUT))
 
         stem.process.launch_tor_with_config(
           config = {
@@ -118,18 +118,18 @@ def main():
 
     for line in msg('msg.startup_banner').splitlines():
       line_format = HEADER_BOLD_OUTPUT if line.startswith('  ') else HEADER_OUTPUT
-      print format(line, *line_format)
+      print(format(line, *line_format))
 
-    print
+    print()
 
     while True:
       try:
         prompt = '... ' if interpreter.is_multiline_context else PROMPT
-        user_input = raw_input(prompt)
+        user_input = input(prompt)
         response = interpreter.run_command(user_input)
 
         if response is not None:
-          print response
+          print(response)
       except (KeyboardInterrupt, EOFError, stem.SocketClosed) as exc:
-        print  # move cursor to the following line
+        print()  # move cursor to the following line
         break
diff --git a/stem/interpreter/arguments.py b/stem/interpreter/arguments.py
index d62a386..d36d618 100644
--- a/stem/interpreter/arguments.py
+++ b/stem/interpreter/arguments.py
@@ -77,7 +77,7 @@ def parse(argv):
 
   # translates our args dict into a named tuple
 
-  Args = collections.namedtuple('Args', args.keys())
+  Args = collections.namedtuple('Args', list(args.keys()))
   return Args(**args)
 
 
diff --git a/stem/interpreter/autocomplete.py b/stem/interpreter/autocomplete.py
index 3a9b40b..c014ef3 100644
--- a/stem/interpreter/autocomplete.py
+++ b/stem/interpreter/autocomplete.py
@@ -70,7 +70,7 @@ def _get_commands(controller, config):
 
   usage_info = config.get('help.usage', {})
 
-  for cmd in usage_info.keys():
+  for cmd in list(usage_info.keys()):
     commands.append('/help ' + cmd)
 
   return commands
diff --git a/stem/interpreter/commands.py b/stem/interpreter/commands.py
index 2679e4c..49759fa 100644
--- a/stem/interpreter/commands.py
+++ b/stem/interpreter/commands.py
@@ -69,7 +69,7 @@ def _get_fingerprint(arg, controller):
     if len(matches) == 0:
       raise ValueError('No relays found at %s' % arg)
     elif len(matches) == 1:
-      return matches.values()[0]
+      return list(matches.values())[0]
     else:
       response = "There's multiple relays at %s, include a port to specify which.\n\n" % arg
 
@@ -118,10 +118,10 @@ class ControlInterpretor(code.InteractiveConsole):
 
   def get_events(self, *event_types):
     events = list(self._received_events)
-    event_types = map(str.upper, event_types)  # make filtering case insensitive
+    event_types = list(map(str.upper, event_types))  # make filtering case insensitive
 
     if event_types:
-      events = filter(lambda e: e.type in event_types, events)
+      events = [e for e in events if e.type in event_types]
 
     return events
 
diff --git a/stem/interpreter/help.py b/stem/interpreter/help.py
index 983da3e..174bc43 100644
--- a/stem/interpreter/help.py
+++ b/stem/interpreter/help.py
@@ -97,7 +97,7 @@ def _response(controller, arg, config):
   elif arg == 'SIGNAL':
     signal_options = config.get('help.signal.options', {})
 
-    for signal, summary in signal_options.items():
+    for signal, summary in list(signal_options.items()):
       output += format('%-15s' % signal, *BOLD_OUTPUT)
       output += format(' - %s' % summary, *STANDARD_OUTPUT) + '\n'
   elif arg == 'SETEVENTS':
diff --git a/stem/process.py b/stem/process.py
index af66cce..de63952 100644
--- a/stem/process.py
+++ b/stem/process.py
@@ -231,7 +231,7 @@ def launch_tor_with_config(config, tor_cmd = 'tor', completion_percent = 100, in
 
   try:
     with open(torrc_path, 'w') as torrc_file:
-      for key, values in config.items():
+      for key, values in list(config.items()):
         if isinstance(values, str):
           torrc_file.write('%s %s\n' % (key, values))
         else:
diff --git a/stem/response/__init__.py b/stem/response/__init__.py
index 8585ac4..38d7d8e 100644
--- a/stem/response/__init__.py
+++ b/stem/response/__init__.py
@@ -43,9 +43,13 @@ __all__ = [
 ]
 
 import re
-import StringIO
 import threading
 
+try:
+  from StringIO import StringIO
+except ImportError:
+  from io import StringIO
+
 import stem.socket
 
 KEY_ARG = re.compile('^(\S+)=')
@@ -149,7 +153,7 @@ class ControlMessage(object):
     :returns: stem.response.ControlMessage instance
     """
 
-    msg = stem.socket.recv_message(StringIO.StringIO(content))
+    msg = stem.socket.recv_message(StringIO(content))
 
     if msg_type is not None:
       convert(msg_type, msg, **kwargs)
@@ -528,7 +532,7 @@ def _unescape(entry):
     #
     #   (unescaped prefix, remaining entry)
 
-    for esc_sequence, replacement in CONTROL_ESCAPES.items():
+    for esc_sequence, replacement in list(CONTROL_ESCAPES.items()):
       if entry.startswith(esc_sequence):
         return (replacement, entry[len(esc_sequence):])
 
diff --git a/stem/response/events.py b/stem/response/events.py
index 49a0779..01a7b26 100644
--- a/stem/response/events.py
+++ b/stem/response/events.py
@@ -12,6 +12,7 @@ import stem.response
 import stem.version
 
 from stem.util import connection, log, str_tools, tor_tools
+from stem._compat import unicode, long
 
 # Matches keyword=value arguments. This can't be a simple "(.*)=(.*)" pattern
 # because some positional arguments, like circuit paths, can have an equal
@@ -122,7 +123,7 @@ class Event(stem.response.ControlMessage):
 
       setattr(self, attr_name, attr_value)
 
-    for controller_attr_name, attr_name in self._KEYWORD_ARGS.items():
+    for controller_attr_name, attr_name in list(self._KEYWORD_ARGS.items()):
       setattr(self, attr_name, self.keyword_args.get(controller_attr_name))
 
   # method overwritten by our subclasses for special handling that they do
diff --git a/stem/socket.py b/stem/socket.py
index 7991899..e308c8a 100644
--- a/stem/socket.py
+++ b/stem/socket.py
@@ -69,6 +69,7 @@ Tor...
 
 from __future__ import absolute_import
 
+
 import re
 import socket
 import threading
diff --git a/stem/util/conf.py b/stem/util/conf.py
index aafb140..c74361c 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -359,7 +359,7 @@ def parse_enum_csv(key, value, enumeration, count = None):
     raise ValueError("The count must be None, an int, or two value tuple. Got '%s' (%s)'" % (count, type(count)))
 
   result = []
-  enum_keys = [k.upper() for k in enumeration.keys()]
+  enum_keys = [k.upper() for k in list(enumeration.keys())]
   enum_values = list(enumeration)
 
   for val in values:
@@ -576,7 +576,7 @@ class Config(object):
       self._listeners.append(listener)
 
       if backfill:
-        for key in self.keys():
+        for key in list(self.keys()):
           listener(self, key)
 
   def clear_listeners(self):
@@ -593,7 +593,7 @@ class Config(object):
     :returns: **list** if strings for the configuration keys we've loaded
     """
 
-    return self._contents.keys()
+    return list(self._contents.keys())
 
   def unused_keys(self):
     """
diff --git a/stem/util/connection.py b/stem/util/connection.py
index 181d148..276f97c 100644
--- a/stem/util/connection.py
+++ b/stem/util/connection.py
@@ -51,6 +51,7 @@ import stem.util.proc
 import stem.util.system
 
 from stem.util import conf, enum, log
+from stem._compat import unicode
 
 # Connection resolution is risky to log about since it's highly likely to
 # contain sensitive information. That said, it's also difficult to get right in
@@ -271,7 +272,7 @@ def system_resolvers(system = None):
 
   # remove any that aren't in the user's PATH
 
-  resolvers = filter(lambda r: stem.util.system.is_available(RESOLVER_COMMAND[r]), resolvers)
+  resolvers = [r for r in resolvers if stem.util.system.is_available(RESOLVER_COMMAND[r])]
 
   # proc resolution, by far, outperforms the others so defaults to this is able
 
@@ -303,7 +304,7 @@ def port_usage(port):
       config.load(config_path)
       port_uses = {}
 
-      for key, value in config.get('port', {}).items():
+      for key, value in list(config.get('port', {}).items()):
         if key.isdigit():
          port_uses[int(key)] = value
         elif '-' in key:
@@ -484,7 +485,7 @@ def expand_ipv6_address(address):
     address = address.replace('::', '::' + ':' * missing_groups)
 
   # inserts missing zeros
-  for index in xrange(8):
+  for index in range(8):
     start = index * 5
     end = address.index(':', start) if index != 7 else len(address)
     missing_zeros = 4 - (end - start)
@@ -515,7 +516,7 @@ def get_mask_ipv4(bits):
   mask_bin = _get_binary(2 ** bits - 1, 32)[::-1]
 
   # breaks it into eight character groupings
-  octets = [mask_bin[8 * i:8 * (i + 1)] for i in xrange(4)]
+  octets = [mask_bin[8 * i:8 * (i + 1)] for i in range(4)]
 
   # converts each octet into its integer value
   return '.'.join([str(int(octet, 2)) for octet in octets])
@@ -542,7 +543,7 @@ def get_mask_ipv6(bits):
   mask_bin = _get_binary(2 ** bits - 1, 128)[::-1]
 
   # breaks it into sixteen character groupings
-  groupings = [mask_bin[16 * i:16 * (i + 1)] for i in xrange(8)]
+  groupings = [mask_bin[16 * i:16 * (i + 1)] for i in range(8)]
 
   # converts each group into its hex value
   return ':'.join(['%04x' % int(group, 2) for group in groupings]).upper()
diff --git a/stem/util/enum.py b/stem/util/enum.py
index 473f0de..7520bda 100644
--- a/stem/util/enum.py
+++ b/stem/util/enum.py
@@ -40,6 +40,8 @@ constructed as simple type listings...
     +- __iter__ - iterator over our enum keys
 """
 
+from stem._compat import unicode
+
 
 def UppercaseEnum(*args):
   """
@@ -158,7 +160,7 @@ class Enum(object):
     if item in vars(self):
       return getattr(self, item)
     else:
-      keys = ', '.join(self.keys())
+      keys = ', '.join(list(self.keys()))
       raise ValueError("'%s' isn't among our enumeration keys, which includes: %s" % (item, keys))
 
   def __iter__(self):
diff --git a/stem/util/log.py b/stem/util/log.py
index 60122de..11a0392 100644
--- a/stem/util/log.py
+++ b/stem/util/log.py
@@ -235,7 +235,7 @@ class _StdoutLogger(logging.Handler):
       datefmt = '%m/%d/%Y %H:%M:%S')
 
   def emit(self, record):
-    print self.formatter.format(record)
+    print(self.formatter.format(record))
 
 
 def log_to_stdout(runlevel):
diff --git a/stem/util/ordereddict.py b/stem/util/ordereddict.py
index 07c7d4e..f3f278a 100644
--- a/stem/util/ordereddict.py
+++ b/stem/util/ordereddict.py
@@ -76,9 +76,9 @@ class OrderedDict(dict, DictMixin):
     if not self:
       raise KeyError('dictionary is empty')
     if last:
-      key = reversed(self).next()
+      key = next(reversed(self))
     else:
-      key = iter(self).next()
+      key = next(iter(self))
     value = self.pop(key)
     return key, value
 
@@ -107,7 +107,7 @@ class OrderedDict(dict, DictMixin):
   def __repr__(self):
     if not self:
       return '%s()' % (self.__class__.__name__,)
-    return '%s(%r)' % (self.__class__.__name__, self.items())
+    return '%s(%r)' % (self.__class__.__name__, list(self.items()))
 
   def copy(self):
     return self.__class__(self)
@@ -123,7 +123,7 @@ class OrderedDict(dict, DictMixin):
     if isinstance(other, OrderedDict):
       if len(self) != len(other):
         return False
-      for p, q in zip(self.items(), other.items()):
+      for p, q in zip(list(self.items()), list(other.items())):
         if p != q:
           return False
       return True
diff --git a/stem/util/str_tools.py b/stem/util/str_tools.py
index 43d12d6..3556a33 100644
--- a/stem/util/str_tools.py
+++ b/stem/util/str_tools.py
@@ -28,6 +28,7 @@ import sys
 
 import stem.prereq
 import stem.util.enum
+from stem._compat import unicode
 
 
 # label conversion tuples of the form...
@@ -213,7 +214,7 @@ def crop(msg, size, min_word_length = 4, min_crop = 0, ending = Ending.ELLIPSE,
     min_word_length += 1
 
   if min_word_length is None:
-    min_word_length = sys.maxint
+    min_word_length = sys.maxsize
 
   # checks if there isn't the minimum space needed to include anything
 
diff --git a/stem/util/system.py b/stem/util/system.py
index 5eedfda..ec33c2b 100644
--- a/stem/util/system.py
+++ b/stem/util/system.py
@@ -56,6 +56,7 @@ import stem.util.str_tools
 
 from stem import UNDEFINED
 from stem.util import log
+from stem._compat import unicode
 
 # Mapping of commands to if they're available or not.
 
@@ -242,7 +243,7 @@ def is_running(command):
       command_listing = call(secondary_resolver, None)
 
     if command_listing:
-      command_listing = map(unicode.strip, command_listing)
+      command_listing = list(map(unicode.strip, command_listing))
       return command in command_listing
 
   return None
@@ -327,7 +328,7 @@ def pid_by_name(process_name, multiple = False):
 
     if results:
       try:
-        pids = map(int, results)
+        pids = list(map(int, results))
 
         if multiple:
           return pids
@@ -348,7 +349,7 @@ def pid_by_name(process_name, multiple = False):
 
     if results and len(results) == 1:
       try:
-        pids = map(int, results[0].split())
+        pids = list(map(int, results[0].split()))
 
         if multiple:
           return pids
@@ -380,7 +381,7 @@ def pid_by_name(process_name, multiple = False):
 
       if results:
         try:
-          pids = map(int, results[1:])
+          pids = list(map(int, results[1:]))
 
           if multiple:
             return pids
@@ -398,7 +399,7 @@ def pid_by_name(process_name, multiple = False):
         results = [r.split()[0] for r in results if r.endswith(' %s' % process_name)]
 
         try:
-          pids = map(int, results)
+          pids = list(map(int, results))
 
           if multiple:
             return pids
@@ -426,7 +427,7 @@ def pid_by_name(process_name, multiple = False):
 
     if results:
       try:
-        pids = map(int, results)
+        pids = list(map(int, results))
 
         if multiple:
           return pids
@@ -966,7 +967,7 @@ def get_process_name():
 
       args, argc = [], argc_t()
 
-      for i in xrange(100):
+      for i in range(100):
         # The ending index can be either None or raise a ValueError when
         # accessed...
         #
diff --git a/stem/util/test_tools.py b/stem/util/test_tools.py
index 8209c58..624f445 100644
--- a/stem/util/test_tools.py
+++ b/stem/util/test_tools.py
@@ -262,7 +262,7 @@ def pyflakes_issues(paths):
         # Paths in pyflakes_ignore are relative, so we need to check to see if our
         # path ends with any of them.
 
-        for ignored_path, ignored_issues in self._ignored_issues.items():
+        for ignored_path, ignored_issues in list(self._ignored_issues.items()):
           if path.endswith(ignored_path) and issue in ignored_issues:
             return True
 
diff --git a/test/integ/connection/connect.py b/test/integ/connection/connect.py
index 4361c0f..c1785fd 100644
--- a/test/integ/connection/connect.py
+++ b/test/integ/connection/connect.py
@@ -2,10 +2,14 @@
 Integration tests for the connect_* convenience functions.
 """
 
-import StringIO
 import sys
 import unittest
 
+try:
+  from StringIO import StringIO
+except ImportError:
+  from io import StringIO
+
 import stem.connection
 import test.runner
 
@@ -14,7 +18,7 @@ class TestConnect(unittest.TestCase):
   def setUp(self):
     # prevents the function from printing to the real stdout
     self.original_stdout = sys.stdout
-    sys.stdout = StringIO.StringIO()
+    sys.stdout = StringIO()
 
   def tearDown(self):
     sys.stdout = self.original_stdout
@@ -58,7 +62,7 @@ class TestConnect(unittest.TestCase):
       test.runner.exercise_controller(self, control_socket)
       control_socket.close()
     else:
-      self.assertEquals(control_socket, None)
+      self.assertEqual(control_socket, None)
 
   def test_connect_socket_file(self):
     """
@@ -80,4 +84,4 @@ class TestConnect(unittest.TestCase):
       test.runner.exercise_controller(self, control_socket)
       control_socket.close()
     else:
-      self.assertEquals(control_socket, None)
+      self.assertEqual(control_socket, None)
diff --git a/test/integ/control/base_controller.py b/test/integ/control/base_controller.py
index 96fb75b..9f0e2aa 100644
--- a/test/integ/control/base_controller.py
+++ b/test/integ/control/base_controller.py
@@ -50,7 +50,7 @@ class TestBaseController(unittest.TestCase):
     with test.runner.get_runner().get_tor_socket() as control_socket:
       controller = stem.control.BaseController(control_socket)
 
-      for _ in xrange(250):
+      for _ in range(250):
         controller.connect()
         controller.close()
 
@@ -77,7 +77,7 @@ class TestBaseController(unittest.TestCase):
     with test.runner.get_runner().get_tor_socket() as control_socket:
       controller = stem.control.BaseController(control_socket)
       response = controller.msg('invalid')
-      self.assertEquals('Unrecognized command "invalid"', str(response))
+      self.assertEqual('Unrecognized command "invalid"', str(response))
 
   def test_msg_invalid_getinfo(self):
     """
@@ -90,7 +90,7 @@ class TestBaseController(unittest.TestCase):
     with test.runner.get_runner().get_tor_socket() as control_socket:
       controller = stem.control.BaseController(control_socket)
       response = controller.msg('GETINFO blarg')
-      self.assertEquals('Unrecognized key "blarg"', str(response))
+      self.assertEqual('Unrecognized key "blarg"', str(response))
 
   def test_msg_repeatedly(self):
     """
@@ -108,7 +108,7 @@ class TestBaseController(unittest.TestCase):
       controller = stem.control.BaseController(control_socket)
 
       def run_getinfo():
-        for _ in xrange(150):
+        for _ in range(150):
           try:
             controller.msg('GETINFO version')
             controller.msg('GETINFO blarg')
@@ -118,13 +118,13 @@ class TestBaseController(unittest.TestCase):
 
       message_threads = []
 
-      for _ in xrange(5):
+      for _ in range(5):
         msg_thread = threading.Thread(target = run_getinfo)
         message_threads.append(msg_thread)
         msg_thread.setDaemon(True)
         msg_thread.start()
 
-      for index in xrange(100):
+      for index in range(100):
         controller.connect()
         controller.close()
 
@@ -187,7 +187,7 @@ class TestBaseController(unittest.TestCase):
       for bw_event in controller.received_events:
         self.assertTrue(re.match('BW [0-9]+ [0-9]+', str(bw_event)))
         self.assertTrue(re.match('650 BW [0-9]+ [0-9]+\r\n', bw_event.raw_content()))
-        self.assertEquals(('650', ' '), bw_event.content()[0][:2])
+        self.assertEqual(('650', ' '), bw_event.content()[0][:2])
 
   def test_get_latest_heartbeat(self):
     """
@@ -219,15 +219,15 @@ class TestBaseController(unittest.TestCase):
       controller.add_status_listener(state_observer.listener, False)
 
       controller.close()
-      self.assertEquals(controller, state_observer.controller)
-      self.assertEquals(stem.control.State.CLOSED, state_observer.state)
+      self.assertEqual(controller, state_observer.controller)
+      self.assertEqual(stem.control.State.CLOSED, state_observer.state)
       self.assertTrue(state_observer.timestamp <= time.time())
       self.assertTrue(state_observer.timestamp > time.time() - 1.0)
       state_observer.reset()
 
       controller.connect()
-      self.assertEquals(controller, state_observer.controller)
-      self.assertEquals(stem.control.State.INIT, state_observer.state)
+      self.assertEqual(controller, state_observer.controller)
+      self.assertEqual(stem.control.State.INIT, state_observer.state)
       self.assertTrue(state_observer.timestamp <= time.time())
       self.assertTrue(state_observer.timestamp > time.time() - 1.0)
       state_observer.reset()
@@ -235,8 +235,8 @@ class TestBaseController(unittest.TestCase):
       # cause the socket to shut down without calling close()
       controller.msg('Blarg!')
       self.assertRaises(stem.SocketClosed, controller.msg, 'blarg')
-      self.assertEquals(controller, state_observer.controller)
-      self.assertEquals(stem.control.State.CLOSED, state_observer.state)
+      self.assertEqual(controller, state_observer.controller)
+      self.assertEqual(stem.control.State.CLOSED, state_observer.state)
       self.assertTrue(state_observer.timestamp <= time.time())
       self.assertTrue(state_observer.timestamp > time.time() - 1.0)
       state_observer.reset()
@@ -244,9 +244,9 @@ class TestBaseController(unittest.TestCase):
       # remove listener and make sure we don't get further notices
       controller.remove_status_listener(state_observer.listener)
       controller.connect()
-      self.assertEquals(None, state_observer.controller)
-      self.assertEquals(None, state_observer.state)
-      self.assertEquals(None, state_observer.timestamp)
+      self.assertEqual(None, state_observer.controller)
+      self.assertEqual(None, state_observer.state)
+      self.assertEqual(None, state_observer.timestamp)
       state_observer.reset()
 
       # add with spawn as true, we need a little delay on this since we then
@@ -255,8 +255,8 @@ class TestBaseController(unittest.TestCase):
       controller.add_status_listener(state_observer.listener, True)
       controller.close()
       time.sleep(0.1)  # not much work going on so this doesn't need to be much
-      self.assertEquals(controller, state_observer.controller)
-      self.assertEquals(stem.control.State.CLOSED, state_observer.state)
+      self.assertEqual(controller, state_observer.controller)
+      self.assertEqual(stem.control.State.CLOSED, state_observer.state)
       self.assertTrue(state_observer.timestamp <= time.time())
       self.assertTrue(state_observer.timestamp > time.time() - 1.0)
       state_observer.reset()
diff --git a/test/integ/control/controller.py b/test/integ/control/controller.py
index cd0b578..fbe66e5 100644
--- a/test/integ/control/controller.py
+++ b/test/integ/control/controller.py
@@ -420,7 +420,7 @@ class TestController(unittest.TestCase):
       self.assertEqual(expected, controller.get_conf_map([config_key], 'la-di-dah'))
 
       request_params = ['ControlPORT', 'dirport', 'datadirectory']
-      reply_params = controller.get_conf_map(request_params, multiple=False).keys()
+      reply_params = list(controller.get_conf_map(request_params, multiple=False).keys())
       self.assertEqual(set(request_params), set(reply_params))
 
       # queries an option that is unset
@@ -974,7 +974,7 @@ class TestController(unittest.TestCase):
 
       # try up to 10 times to rule out transient network failures
 
-      for _ in xrange(10):
+      for _ in range(10):
         try:
           s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
           s.settimeout(30)
@@ -1184,7 +1184,7 @@ class TestController(unittest.TestCase):
 
     with test.runner.get_runner().get_tor_controller() as controller:
       # try 10 times to build a circuit we can connect through
-      for i in xrange(10):
+      for i in range(10):
         controller.add_event_listener(handle_streamcreated, stem.control.EventType.STREAM)
         controller.set_conf('__LeaveStreamsUnattached', '1')
 
@@ -1208,7 +1208,7 @@ class TestController(unittest.TestCase):
     self.assertTrue(our_stream.circ_id)
     self.assertTrue(circuit_id)
 
-    self.assertEquals(our_stream.circ_id, circuit_id)
+    self.assertEqual(our_stream.circ_id, circuit_id)
 
   def test_get_circuits(self):
     """
diff --git a/test/integ/descriptor/remote.py b/test/integ/descriptor/remote.py
index d1a377c..d1700a5 100644
--- a/test/integ/descriptor/remote.py
+++ b/test/integ/descriptor/remote.py
@@ -31,7 +31,7 @@ class TestDescriptorDownloader(unittest.TestCase):
 
     queries = []
 
-    for nickname, authority in stem.descriptor.remote.get_authorities().items():
+    for nickname, authority in list(stem.descriptor.remote.get_authorities().items()):
       queries.append((stem.descriptor.remote.Query(
         '/tor/server/fp/9695DFC35FFEB861329B9F1AB04C46397020CE31',
         'server-descriptor 1.0',
diff --git a/test/integ/descriptor/server_descriptor.py b/test/integ/descriptor/server_descriptor.py
index 6882c58..d291923 100644
--- a/test/integ/descriptor/server_descriptor.py
+++ b/test/integ/descriptor/server_descriptor.py
@@ -32,10 +32,10 @@ class TestServerDescriptor(unittest.TestCase):
     with open(descriptor_path, 'rb') as descriptor_file:
       for desc in stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'):
         # the following attributes should be deprecated, and not appear in the wild
-        self.assertEquals(None, desc.read_history_end)
-        self.assertEquals(None, desc.write_history_end)
-        self.assertEquals(None, desc.eventdns)
-        self.assertEquals(None, desc.socks_port)
+        self.assertEqual(None, desc.read_history_end)
+        self.assertEqual(None, desc.write_history_end)
+        self.assertEqual(None, desc.eventdns)
+        self.assertEqual(None, desc.socks_port)
 
         unrecognized_lines = desc.get_unrecognized_lines()
 
diff --git a/test/integ/process.py b/test/integ/process.py
index b06e500..bd080b6 100644
--- a/test/integ/process.py
+++ b/test/integ/process.py
@@ -59,7 +59,7 @@ class TestProcess(unittest.TestCase):
       # exercises the socket
       control_socket.send('GETCONF ControlPort')
       getconf_response = control_socket.recv()
-      self.assertEquals('ControlPort=2778', str(getconf_response))
+      self.assertEqual('ControlPort=2778', str(getconf_response))
     finally:
       if control_socket:
         control_socket.close()
@@ -121,7 +121,7 @@ class TestProcess(unittest.TestCase):
     # tor polls for the process every fifteen seconds so this may take a
     # while...
 
-    for seconds_waited in xrange(30):
+    for seconds_waited in range(30):
       if tor_process.poll() == 0:
         return  # tor exited
 
@@ -158,7 +158,7 @@ class TestProcess(unittest.TestCase):
     controller.close()
 
     # give tor a few seconds to quit
-    for seconds_waited in xrange(5):
+    for seconds_waited in range(5):
       if tor_process.poll() == 0:
         return  # tor exited
 
diff --git a/test/integ/socket/control_message.py b/test/integ/socket/control_message.py
index bdebb11..a2eb6f2 100644
--- a/test/integ/socket/control_message.py
+++ b/test/integ/socket/control_message.py
@@ -27,10 +27,10 @@ class TestControlMessage(unittest.TestCase):
     control_socket.send('GETINFO version')
 
     auth_required_response = control_socket.recv()
-    self.assertEquals('Authentication required.', str(auth_required_response))
-    self.assertEquals(['Authentication required.'], list(auth_required_response))
-    self.assertEquals('514 Authentication required.\r\n', auth_required_response.raw_content())
-    self.assertEquals([('514', ' ', 'Authentication required.')], auth_required_response.content())
+    self.assertEqual('Authentication required.', str(auth_required_response))
+    self.assertEqual(['Authentication required.'], list(auth_required_response))
+    self.assertEqual('514 Authentication required.\r\n', auth_required_response.raw_content())
+    self.assertEqual([('514', ' ', 'Authentication required.')], auth_required_response.content())
 
     # The socket's broken but doesn't realize it yet. These use cases are
     # checked in more depth by the ControlSocket integ tests.
@@ -65,10 +65,10 @@ class TestControlMessage(unittest.TestCase):
     with test.runner.get_runner().get_tor_socket() as control_socket:
       control_socket.send('blarg')
       unrecognized_command_response = control_socket.recv()
-      self.assertEquals('Unrecognized command "blarg"', str(unrecognized_command_response))
-      self.assertEquals(['Unrecognized command "blarg"'], list(unrecognized_command_response))
-      self.assertEquals('510 Unrecognized command "blarg"\r\n', unrecognized_command_response.raw_content())
-      self.assertEquals([('510', ' ', 'Unrecognized command "blarg"')], unrecognized_command_response.content())
+      self.assertEqual('Unrecognized command "blarg"', str(unrecognized_command_response))
+      self.assertEqual(['Unrecognized command "blarg"'], list(unrecognized_command_response))
+      self.assertEqual('510 Unrecognized command "blarg"\r\n', unrecognized_command_response.raw_content())
+      self.assertEqual([('510', ' ', 'Unrecognized command "blarg"')], unrecognized_command_response.content())
 
   def test_invalid_getinfo(self):
     """
@@ -81,10 +81,10 @@ class TestControlMessage(unittest.TestCase):
     with test.runner.get_runner().get_tor_socket() as control_socket:
       control_socket.send('GETINFO blarg')
       unrecognized_key_response = control_socket.recv()
-      self.assertEquals('Unrecognized key "blarg"', str(unrecognized_key_response))
-      self.assertEquals(['Unrecognized key "blarg"'], list(unrecognized_key_response))
-      self.assertEquals('552 Unrecognized key "blarg"\r\n', unrecognized_key_response.raw_content())
-      self.assertEquals([('552', ' ', 'Unrecognized key "blarg"')], unrecognized_key_response.content())
+      self.assertEqual('Unrecognized key "blarg"', str(unrecognized_key_response))
+      self.assertEqual(['Unrecognized key "blarg"'], list(unrecognized_key_response))
+      self.assertEqual('552 Unrecognized key "blarg"\r\n', unrecognized_key_response.raw_content())
+      self.assertEqual([('552', ' ', 'Unrecognized key "blarg"')], unrecognized_key_response.content())
 
   def test_getinfo_config_file(self):
     """
@@ -100,10 +100,10 @@ class TestControlMessage(unittest.TestCase):
     with runner.get_tor_socket() as control_socket:
       control_socket.send('GETINFO config-file')
       config_file_response = control_socket.recv()
-      self.assertEquals('config-file=%s\nOK' % torrc_dst, str(config_file_response))
-      self.assertEquals(['config-file=%s' % torrc_dst, 'OK'], list(config_file_response))
-      self.assertEquals('250-config-file=%s\r\n250 OK\r\n' % torrc_dst, config_file_response.raw_content())
-      self.assertEquals([('250', '-', 'config-file=%s' % torrc_dst), ('250', ' ', 'OK')], config_file_response.content())
+      self.assertEqual('config-file=%s\nOK' % torrc_dst, str(config_file_response))
+      self.assertEqual(['config-file=%s' % torrc_dst, 'OK'], list(config_file_response))
+      self.assertEqual('250-config-file=%s\r\n250 OK\r\n' % torrc_dst, config_file_response.raw_content())
+      self.assertEqual([('250', '-', 'config-file=%s' % torrc_dst), ('250', ' ', 'OK')], config_file_response.content())
 
   def test_getinfo_config_text(self):
     """
@@ -161,10 +161,10 @@ class TestControlMessage(unittest.TestCase):
     with test.runner.get_runner().get_tor_socket() as control_socket:
       control_socket.send('SETEVENTS BW')
       setevents_response = control_socket.recv()
-      self.assertEquals('OK', str(setevents_response))
-      self.assertEquals(['OK'], list(setevents_response))
-      self.assertEquals('250 OK\r\n', setevents_response.raw_content())
-      self.assertEquals([('250', ' ', 'OK')], setevents_response.content())
+      self.assertEqual('OK', str(setevents_response))
+      self.assertEqual(['OK'], list(setevents_response))
+      self.assertEqual('250 OK\r\n', setevents_response.raw_content())
+      self.assertEqual([('250', ' ', 'OK')], setevents_response.content())
 
       # Tor will emit a BW event once per second. Parsing two of them.
 
@@ -172,4 +172,4 @@ class TestControlMessage(unittest.TestCase):
         bw_event = control_socket.recv()
         self.assertTrue(re.match('BW [0-9]+ [0-9]+', str(bw_event)))
         self.assertTrue(re.match('650 BW [0-9]+ [0-9]+\r\n', bw_event.raw_content()))
-        self.assertEquals(('650', ' '), bw_event.content()[0][:2])
+        self.assertEqual(('650', ' '), bw_event.content()[0][:2])
diff --git a/test/integ/socket/control_socket.py b/test/integ/socket/control_socket.py
index f0f2e40..a45c47e 100644
--- a/test/integ/socket/control_socket.py
+++ b/test/integ/socket/control_socket.py
@@ -104,7 +104,7 @@ class TestControlSocket(unittest.TestCase):
 
     with test.runner.get_runner().get_tor_socket() as control_socket:
       control_socket.send('QUIT')
-      self.assertEquals('closing connection', str(control_socket.recv()))
+      self.assertEqual('closing connection', str(control_socket.recv()))
       self.assertTrue(control_socket.is_alive())
 
       # If we send another message to a port based socket then it will seem to
@@ -143,7 +143,7 @@ class TestControlSocket(unittest.TestCase):
 
     with test.runner.get_runner().get_tor_socket() as control_socket:
       control_socket.send('QUIT')
-      self.assertEquals('closing connection', str(control_socket.recv()))
+      self.assertEqual('closing connection', str(control_socket.recv()))
 
       # Neither a port or file based socket will know that tor has hung up on
       # the connection at this point. We should know after calling recv(),
diff --git a/test/integ/util/conf.py b/test/integ/util/conf.py
index 2a50129..daf566d 100644
--- a/test/integ/util/conf.py
+++ b/test/integ/util/conf.py
@@ -95,11 +95,11 @@ class TestConf(unittest.TestCase):
     user_config = stem.util.conf.get_config('integ_testing')
     user_config.load(test_config_path)
 
-    self.assertEquals('atagar', ssh_config['login.user'])
-    self.assertEquals('pepperjack_is_awesome!', ssh_config['login.password'])
-    self.assertEquals('1.2.3.4', ssh_config['destination.ip'])
-    self.assertEquals(22, ssh_config['destination.port'])
-    self.assertEquals(['export PATH=$PATH:~/bin', 'alias l=ls'], ssh_config['startup.run'])
+    self.assertEqual('atagar', ssh_config['login.user'])
+    self.assertEqual('pepperjack_is_awesome!', ssh_config['login.password'])
+    self.assertEqual('1.2.3.4', ssh_config['destination.ip'])
+    self.assertEqual(22, ssh_config['destination.port'])
+    self.assertEqual(['export PATH=$PATH:~/bin', 'alias l=ls'], ssh_config['startup.run'])
 
   def test_load_multiline(self):
     """
@@ -111,9 +111,9 @@ class TestConf(unittest.TestCase):
     test_config.load(test_config_path)
 
     for entry in ('simple', 'leading_whitespace', 'squashed_top', 'squashed_bottom'):
-      self.assertEquals('la de da\nand a ho hum', test_config.get('multiline.entry.%s' % entry))
+      self.assertEqual('la de da\nand a ho hum', test_config.get('multiline.entry.%s' % entry))
 
-    self.assertEquals('', test_config.get('multiline.entry.empty'))
+    self.assertEqual('', test_config.get('multiline.entry.empty'))
 
   def test_save(self):
     """
@@ -133,6 +133,6 @@ class TestConf(unittest.TestCase):
     test_config.clear()
     test_config.load()
 
-    self.assertEquals("yup, I'm there", test_config.get_value('single_value'))
-    self.assertEquals(['a', 'b', 'c'], test_config.get_value('multiple_values', multiple = True))
-    self.assertEquals(HERALD_POEM, test_config.get_value('multiline_value'))
+    self.assertEqual("yup, I'm there", test_config.get_value('single_value'))
+    self.assertEqual(['a', 'b', 'c'], test_config.get_value('multiple_values', multiple = True))
+    self.assertEqual(HERALD_POEM, test_config.get_value('multiline_value'))
diff --git a/test/integ/util/proc.py b/test/integ/util/proc.py
index 1047912..66203dd 100644
--- a/test/integ/util/proc.py
+++ b/test/integ/util/proc.py
@@ -26,7 +26,7 @@ class TestProc(unittest.TestCase):
 
     runner = test.runner.get_runner()
     runner_pid, tor_cwd = runner.get_pid(), runner.get_tor_cwd()
-    self.assertEquals(tor_cwd, proc.cwd(runner_pid))
+    self.assertEqual(tor_cwd, proc.cwd(runner_pid))
 
   def test_uid(self):
     """
@@ -38,7 +38,7 @@ class TestProc(unittest.TestCase):
       return
 
     tor_pid = test.runner.get_runner().get_pid()
-    self.assertEquals(os.geteuid(), proc.uid(tor_pid))
+    self.assertEqual(os.geteuid(), proc.uid(tor_pid))
 
   def test_memory_usage(self):
     """
@@ -68,7 +68,7 @@ class TestProc(unittest.TestCase):
     tor_pid = test.runner.get_runner().get_pid()
     command, utime, stime, start_time = proc.stats(tor_pid, 'command', 'utime', 'stime', 'start time')
 
-    self.assertEquals('tor', command)
+    self.assertEqual('tor', command)
     self.assertTrue(float(utime) > 0)
     self.assertTrue(float(stime) >= 0)
     self.assertTrue(float(start_time) > proc.system_start_time())
diff --git a/test/integ/util/system.py b/test/integ/util/system.py
index e5cb199..d754ddb 100644
--- a/test/integ/util/system.py
+++ b/test/integ/util/system.py
@@ -90,8 +90,8 @@ class TestSystem(unittest.TestCase):
       return
 
     tor_pid = test.runner.get_runner().get_pid()
-    self.assertEquals(tor_pid, stem.util.system.pid_by_name('tor'))
-    self.assertEquals(None, stem.util.system.pid_by_name('blarg_and_stuff'))
+    self.assertEqual(tor_pid, stem.util.system.pid_by_name('tor'))
+    self.assertEqual(None, stem.util.system.pid_by_name('blarg_and_stuff'))
 
   def test_pid_by_name_pgrep(self):
     """
@@ -113,7 +113,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       tor_pid = test.runner.get_runner().get_pid()
-      self.assertEquals(tor_pid, stem.util.system.pid_by_name('tor'))
+      self.assertEqual(tor_pid, stem.util.system.pid_by_name('tor'))
 
   def test_pid_by_name_pidof(self):
     """
@@ -135,7 +135,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       tor_pid = test.runner.get_runner().get_pid()
-      self.assertEquals(tor_pid, stem.util.system.pid_by_name('tor'))
+      self.assertEqual(tor_pid, stem.util.system.pid_by_name('tor'))
 
   def test_pid_by_name_ps_linux(self):
     """
@@ -160,7 +160,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       tor_pid = test.runner.get_runner().get_pid()
-      self.assertEquals(tor_pid, stem.util.system.pid_by_name('tor'))
+      self.assertEqual(tor_pid, stem.util.system.pid_by_name('tor'))
 
   def test_pid_by_name_ps_bsd(self):
     """
@@ -185,7 +185,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       tor_pid = test.runner.get_runner().get_pid()
-      self.assertEquals(tor_pid, stem.util.system.pid_by_name('tor'))
+      self.assertEqual(tor_pid, stem.util.system.pid_by_name('tor'))
 
   def test_pid_by_name_lsof(self):
     """
@@ -214,7 +214,7 @@ class TestSystem(unittest.TestCase):
       all_tor_pids = stem.util.system.pid_by_name('tor', multiple = True)
 
       if len(all_tor_pids) == 1:
-        self.assertEquals(our_tor_pid, all_tor_pids[0])
+        self.assertEqual(our_tor_pid, all_tor_pids[0])
 
   def test_pid_by_port(self):
     """
@@ -241,8 +241,8 @@ class TestSystem(unittest.TestCase):
       return
 
     tor_pid, tor_port = runner.get_pid(), test.runner.CONTROL_PORT
-    self.assertEquals(tor_pid, stem.util.system.pid_by_port(tor_port))
-    self.assertEquals(None, stem.util.system.pid_by_port(99999))
+    self.assertEqual(tor_pid, stem.util.system.pid_by_port(tor_port))
+    self.assertEqual(None, stem.util.system.pid_by_port(99999))
 
   def test_pid_by_port_netstat(self):
     """
@@ -274,7 +274,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       tor_pid = test.runner.get_runner().get_pid()
-      self.assertEquals(tor_pid, stem.util.system.pid_by_port(test.runner.CONTROL_PORT))
+      self.assertEqual(tor_pid, stem.util.system.pid_by_port(test.runner.CONTROL_PORT))
 
   def test_pid_by_port_sockstat(self):
     """
@@ -303,7 +303,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       tor_pid = test.runner.get_runner().get_pid()
-      self.assertEquals(tor_pid, stem.util.system.pid_by_port(test.runner.CONTROL_PORT))
+      self.assertEqual(tor_pid, stem.util.system.pid_by_port(test.runner.CONTROL_PORT))
 
   def test_pid_by_port_lsof(self):
     """
@@ -332,7 +332,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       tor_pid = test.runner.get_runner().get_pid()
-      self.assertEquals(tor_pid, stem.util.system.pid_by_port(test.runner.CONTROL_PORT))
+      self.assertEqual(tor_pid, stem.util.system.pid_by_port(test.runner.CONTROL_PORT))
 
   def test_pid_by_open_file(self):
     """
@@ -341,11 +341,11 @@ class TestSystem(unittest.TestCase):
 
     # check a directory that exists, but isn't claimed by any application
     tmpdir = tempfile.mkdtemp()
-    self.assertEquals(None, stem.util.system.pid_by_open_file(tmpdir))
+    self.assertEqual(None, stem.util.system.pid_by_open_file(tmpdir))
 
     # check a directory that doesn't exist
     os.rmdir(tmpdir)
-    self.assertEquals(None, stem.util.system.pid_by_open_file(tmpdir))
+    self.assertEqual(None, stem.util.system.pid_by_open_file(tmpdir))
 
   def test_cwd(self):
     """
@@ -362,8 +362,8 @@ class TestSystem(unittest.TestCase):
       return
 
     runner_pid, tor_cwd = runner.get_pid(), runner.get_tor_cwd()
-    self.assertEquals(tor_cwd, stem.util.system.cwd(runner_pid))
-    self.assertEquals(None, stem.util.system.cwd(99999))
+    self.assertEqual(tor_cwd, stem.util.system.cwd(runner_pid))
+    self.assertEqual(None, stem.util.system.cwd(99999))
 
   def test_cwd_pwdx(self):
     """
@@ -389,7 +389,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       runner_pid, tor_cwd = runner.get_pid(), runner.get_tor_cwd()
-      self.assertEquals(tor_cwd, stem.util.system.cwd(runner_pid))
+      self.assertEqual(tor_cwd, stem.util.system.cwd(runner_pid))
 
   def test_cwd_lsof(self):
     """
@@ -415,7 +415,7 @@ class TestSystem(unittest.TestCase):
       call_mock.side_effect = call_replacement
 
       runner_pid, tor_cwd = runner.get_pid(), runner.get_tor_cwd()
-      self.assertEquals(tor_cwd, stem.util.system.cwd(runner_pid))
+      self.assertEqual(tor_cwd, stem.util.system.cwd(runner_pid))
 
   def test_user_none(self):
     """
@@ -504,7 +504,7 @@ class TestSystem(unittest.TestCase):
     properly).
     """
 
-    self.assertEquals(0, stem.util.system.bsd_jail_id(99999))
+    self.assertEqual(0, stem.util.system.bsd_jail_id(99999))
 
   def test_expand_path(self):
     """
@@ -524,15 +524,15 @@ class TestSystem(unittest.TestCase):
       test.runner.skip(self, '(running as root)')
       return
 
-    self.assertEquals(os.getcwd(), stem.util.system.expand_path('.'))
-    self.assertEquals(os.getcwd(), stem.util.system.expand_path('./'))
-    self.assertEquals(os.path.join(os.getcwd(), 'foo'), stem.util.system.expand_path('./foo'))
+    self.assertEqual(os.getcwd(), stem.util.system.expand_path('.'))
+    self.assertEqual(os.getcwd(), stem.util.system.expand_path('./'))
+    self.assertEqual(os.path.join(os.getcwd(), 'foo'), stem.util.system.expand_path('./foo'))
 
     home_dir, username = os.path.expanduser('~'), getpass.getuser()
-    self.assertEquals(home_dir, stem.util.system.expand_path('~'))
-    self.assertEquals(home_dir, stem.util.system.expand_path('~/'))
-    self.assertEquals(home_dir, stem.util.system.expand_path('~%s' % username))
-    self.assertEquals(os.path.join(home_dir, 'foo'), stem.util.system.expand_path('~%s/foo' % username))
+    self.assertEqual(home_dir, stem.util.system.expand_path('~'))
+    self.assertEqual(home_dir, stem.util.system.expand_path('~/'))
+    self.assertEqual(home_dir, stem.util.system.expand_path('~%s' % username))
+    self.assertEqual(os.path.join(home_dir, 'foo'), stem.util.system.expand_path('~%s/foo' % username))
 
   def test_set_process_name(self):
     """
diff --git a/test/integ/version.py b/test/integ/version.py
index 7269ab1..0cd15eb 100644
--- a/test/integ/version.py
+++ b/test/integ/version.py
@@ -43,7 +43,7 @@ class TestVersion(unittest.TestCase):
 
     runner = test.runner.get_runner()
     system_tor_version = stem.version.get_system_tor_version(runner.get_tor_command())
-    self.assertEquals(runner.get_tor_version(), system_tor_version)
+    self.assertEqual(runner.get_tor_version(), system_tor_version)
 
   def test_getinfo_version_parsing(self):
     """
diff --git a/test/mocking.py b/test/mocking.py
index e3715c7..3d6ac16 100644
--- a/test/mocking.py
+++ b/test/mocking.py
@@ -204,8 +204,8 @@ def get_all_combinations(attr, include_empty = False):
     yield ()
 
   seen = set()
-  for index in xrange(1, len(attr) + 1):
-    product_arg = [attr for _ in xrange(index)]
+  for index in range(1, len(attr) + 1):
+    product_arg = [attr for _ in range(index)]
 
     for item in itertools.product(*product_arg):
       # deduplicate, sort, and only provide if we haven't seen it yet
@@ -323,7 +323,7 @@ def _get_descriptor_content(attr = None, exclude = (), header_template = (), foo
 
   remainder = []
 
-  for k, v in attr.items():
+  for k, v in list(attr.items()):
     if v:
       remainder.append('%s %s' % (k, v))
     else:
@@ -605,7 +605,7 @@ def get_network_status_document_v3(attr = None, exclude = (), authorities = None
       'consensus-method': '9',
     }
 
-  for k, v in extra_defaults.items():
+  for k, v in list(extra_defaults.items()):
     if not (k in attr or (exclude and k in exclude)):
       attr[k] = v
 
diff --git a/test/output.py b/test/output.py
index 1435b2f..8b4648f 100644
--- a/test/output.py
+++ b/test/output.py
@@ -62,7 +62,7 @@ def println(msg = '', *attr):
     sys.stdout.write(msg)
     sys.stdout.flush()
   else:
-    print msg
+    print(msg)
 
 
 def print_divider(msg, is_header = False):
@@ -75,7 +75,7 @@ def print_logging(logging_buffer):
     for entry in logging_buffer:
       println(entry.replace('\n', '\n  '), term.Color.MAGENTA)
 
-    print
+    print()
 
 
 def apply_filters(testing_output, *filters):
diff --git a/test/runner.py b/test/runner.py
index 1b55da3..ee261b7 100644
--- a/test/runner.py
+++ b/test/runner.py
@@ -199,7 +199,7 @@ def exercise_controller(test_case, controller):
   else:
     config_file_response = controller.msg('GETINFO config-file')
 
-  test_case.assertEquals('config-file=%s\nOK' % torrc_path, str(config_file_response))
+  test_case.assertEqual('config-file=%s\nOK' % torrc_path, str(config_file_response))
 
 
 def get_runner():
@@ -638,13 +638,13 @@ class Runner(object):
         socket_dir = os.path.dirname(CONTROL_SOCKET_PATH)
         println('  making control socket directory (%s)... ' % socket_dir, STATUS, NO_NL)
 
-        if os.path.exists(socket_dir) and stat.S_IMODE(os.stat(socket_dir).st_mode) == 0700:
+        if os.path.exists(socket_dir) and stat.S_IMODE(os.stat(socket_dir).st_mode) == 0o700:
           println('skipped', STATUS)
         else:
           if not os.path.exists(socket_dir):
             os.makedirs(socket_dir)
 
-          os.chmod(socket_dir, 0700)
+          os.chmod(socket_dir, 0o700)
           println('done', STATUS)
       except OSError as exc:
         println('failed (%s)' % exc, ERROR)
diff --git a/test/unit/connection/connect.py b/test/unit/connection/connect.py
index 0cc5ee4..b342eb7 100644
--- a/test/unit/connection/connect.py
+++ b/test/unit/connection/connect.py
@@ -2,9 +2,13 @@
 Unit tests for the stem.connection.connect function.
 """
 
-import StringIO
 import unittest
 
+try:
+  from StringIO import StringIO
+except ImportError:
+  from io import StringIO
+
 from mock import Mock, patch
 
 import stem
@@ -13,7 +17,7 @@ import stem.socket
 
 
 class TestConnect(unittest.TestCase):
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.util.system.is_running')
   @patch('os.path.exists', Mock(return_value = True))
   @patch('stem.socket.ControlSocketFile', Mock(side_effect = stem.SocketError('failed')))
@@ -26,7 +30,7 @@ class TestConnect(unittest.TestCase):
     is_running_mock.return_value = True
     self._assert_connect_fails_with({}, stdout_mock, "Unable to connect to tor. Maybe it's running without a ControlPort?")
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('os.path.exists')
   @patch('stem.util.system.is_running', Mock(return_value = True))
   @patch('stem.socket.ControlSocketFile', Mock(side_effect = stem.SocketError('failed')))
@@ -114,7 +118,7 @@ class TestConnect(unittest.TestCase):
     authenticate_mock.assert_any_call(control_socket, None, None)
     authenticate_mock.assert_any_call(control_socket, 'my_password', None)
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.connection.authenticate')
   def test_auth_failure(self, authenticate_mock, stdout_mock):
     control_socket = stem.socket.ControlPort(connect = False)
diff --git a/test/unit/control/controller.py b/test/unit/control/controller.py
index 58df2d4..3dd57d3 100644
--- a/test/unit/control/controller.py
+++ b/test/unit/control/controller.py
@@ -585,7 +585,7 @@ class TestControl(unittest.TestCase):
         ],
     }
 
-    for test_input, expected in pydoc_examples.items():
+    for test_input, expected in list(pydoc_examples.items()):
       self.assertEqual(expected, _parse_circ_path(test_input))
 
     # exercise with some invalid inputs
diff --git a/test/unit/descriptor/export.py b/test/unit/descriptor/export.py
index eca2200..7e9e01b 100644
--- a/test/unit/descriptor/export.py
+++ b/test/unit/descriptor/export.py
@@ -2,9 +2,13 @@
 Unit tests for stem.descriptor.export.
 """
 
-import StringIO
 import unittest
 
+try:
+  from StringIO import StringIO
+except ImportError:
+  from io import StringIO
+
 import stem.prereq
 import test.runner
 
@@ -31,11 +35,11 @@ class TestExport(unittest.TestCase):
 
     desc_csv = export_csv(desc, included_fields = ('nickname', 'address', 'published'), header = False)
     expected = 'caerSidi,71.35.133.197,2012-03-01 17:15:27\n'
-    self.assertEquals(expected, desc_csv)
+    self.assertEqual(expected, desc_csv)
 
     desc_csv = export_csv(desc, included_fields = ('nickname', 'address', 'published'), header = True)
     expected = 'nickname,address,published\n' + expected
-    self.assertEquals(expected, desc_csv)
+    self.assertEqual(expected, desc_csv)
 
   def test_multiple_descriptors(self):
     """
@@ -62,7 +66,7 @@ class TestExport(unittest.TestCase):
     desc = get_relay_server_descriptor()
     desc_csv = export_csv(desc)
 
-    csv_buffer = StringIO.StringIO()
+    csv_buffer = StringIO()
     export_csv_file(csv_buffer, desc)
 
     self.assertEqual(desc_csv, csv_buffer.getvalue())
@@ -88,8 +92,7 @@ class TestExport(unittest.TestCase):
     """
     Exercises when we don't provide any descriptors.
     """
-
-    self.assertEquals('', export_csv([]))
+    self.assertEqual('', export_csv([]))
 
   def test_invalid_attributes(self):
     """
diff --git a/test/unit/descriptor/extrainfo_descriptor.py b/test/unit/descriptor/extrainfo_descriptor.py
index a8b0ed7..7e67019 100644
--- a/test/unit/descriptor/extrainfo_descriptor.py
+++ b/test/unit/descriptor/extrainfo_descriptor.py
@@ -29,36 +29,36 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 -----END SIGNATURE-----"""
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'extra-info 1.0'))
-    self.assertEquals('NINJA', desc.nickname)
-    self.assertEquals('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 3, 50), desc.published)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.read_history_end)
-    self.assertEquals(900, desc.read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.write_history_end)
-    self.assertEquals(900, desc.write_history_interval)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_read_history_end)
-    self.assertEquals(900, desc.dir_read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_write_history_end)
-    self.assertEquals(900, desc.dir_write_history_interval)
-    self.assertEquals(expected_signature, desc.signature)
-    self.assertEquals('00A57A9AAB5EA113898E2DD02A755E31AFC27227', desc.digest())
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual('NINJA', desc.nickname)
+    self.assertEqual('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
+    self.assertEqual(datetime.datetime(2012, 5, 5, 17, 3, 50), desc.published)
+    self.assertEqual(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.read_history_end)
+    self.assertEqual(900, desc.read_history_interval)
+    self.assertEqual(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.write_history_end)
+    self.assertEqual(900, desc.write_history_interval)
+    self.assertEqual(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_read_history_end)
+    self.assertEqual(900, desc.dir_read_history_interval)
+    self.assertEqual(datetime.datetime(2012, 5, 5, 17, 2, 45), desc.dir_write_history_end)
+    self.assertEqual(900, desc.dir_write_history_interval)
+    self.assertEqual(expected_signature, desc.signature)
+    self.assertEqual('00A57A9AAB5EA113898E2DD02A755E31AFC27227', desc.digest())
+    self.assertEqual([], desc.get_unrecognized_lines())
 
     # The read-history, write-history, dirreq-read-history, and
     # dirreq-write-history lines are pretty long so just checking
     # the initial contents for the line and parsed values.
 
     read_values_start = [3309568, 9216, 41984, 27648, 123904]
-    self.assertEquals(read_values_start, desc.read_history_values[:5])
+    self.assertEqual(read_values_start, desc.read_history_values[:5])
 
     write_values_start = [1082368, 19456, 50176, 272384, 485376]
-    self.assertEquals(write_values_start, desc.write_history_values[:5])
+    self.assertEqual(write_values_start, desc.write_history_values[:5])
 
     dir_read_values_start = [0, 0, 0, 0, 33792, 27648, 48128]
-    self.assertEquals(dir_read_values_start, desc.dir_read_history_values[:7])
+    self.assertEqual(dir_read_values_start, desc.dir_read_history_values[:7])
 
     dir_write_values_start = [0, 0, 0, 227328, 349184, 382976, 738304]
-    self.assertEquals(dir_write_values_start, desc.dir_write_history_values[:7])
+    self.assertEqual(dir_write_values_start, desc.dir_write_history_values[:7])
 
   def test_metrics_bridge_descriptor(self):
     """
@@ -86,40 +86,40 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     }
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'bridge-extra-info 1.0'))
-    self.assertEquals('ec2bridgereaac65a3', desc.nickname)
-    self.assertEquals('1EC248422B57D9C0BD751892FE787585407479A4', desc.fingerprint)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 21, 27), desc.published)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.read_history_end)
-    self.assertEquals(900, desc.read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.write_history_end)
-    self.assertEquals(900, desc.write_history_interval)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_read_history_end)
-    self.assertEquals(900, desc.dir_read_history_interval)
-    self.assertEquals(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_write_history_end)
-    self.assertEquals(900, desc.dir_write_history_interval)
-    self.assertEquals('00A2AECCEAD3FEE033CFE29893387143146728EC', desc.digest())
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual('ec2bridgereaac65a3', desc.nickname)
+    self.assertEqual('1EC248422B57D9C0BD751892FE787585407479A4', desc.fingerprint)
+    self.assertEqual(datetime.datetime(2012, 6, 8, 2, 21, 27), desc.published)
+    self.assertEqual(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.read_history_end)
+    self.assertEqual(900, desc.read_history_interval)
+    self.assertEqual(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.write_history_end)
+    self.assertEqual(900, desc.write_history_interval)
+    self.assertEqual(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_read_history_end)
+    self.assertEqual(900, desc.dir_read_history_interval)
+    self.assertEqual(datetime.datetime(2012, 6, 8, 2, 10, 38), desc.dir_write_history_end)
+    self.assertEqual(900, desc.dir_write_history_interval)
+    self.assertEqual('00A2AECCEAD3FEE033CFE29893387143146728EC', desc.digest())
+    self.assertEqual([], desc.get_unrecognized_lines())
 
     read_values_start = [337920, 437248, 3995648, 48726016]
-    self.assertEquals(read_values_start, desc.read_history_values[:4])
+    self.assertEqual(read_values_start, desc.read_history_values[:4])
 
     write_values_start = [343040, 991232, 5649408, 49548288]
-    self.assertEquals(write_values_start, desc.write_history_values[:4])
+    self.assertEqual(write_values_start, desc.write_history_values[:4])
 
     dir_read_values_start = [0, 71680, 99328, 25600]
-    self.assertEquals(dir_read_values_start, desc.dir_read_history_values[:4])
+    self.assertEqual(dir_read_values_start, desc.dir_read_history_values[:4])
 
     dir_write_values_start = [5120, 664576, 2419712, 578560]
-    self.assertEquals(dir_write_values_start, desc.dir_write_history_values[:4])
+    self.assertEqual(dir_write_values_start, desc.dir_write_history_values[:4])
 
-    self.assertEquals({}, desc.dir_v2_requests)
-    self.assertEquals({}, desc.dir_v3_requests)
+    self.assertEqual({}, desc.dir_v2_requests)
+    self.assertEqual({}, desc.dir_v3_requests)
 
-    self.assertEquals(expected_dir_v2_responses, desc.dir_v2_responses)
-    self.assertEquals(expected_dir_v3_responses, desc.dir_v3_responses)
+    self.assertEqual(expected_dir_v2_responses, desc.dir_v2_responses)
+    self.assertEqual(expected_dir_v3_responses, desc.dir_v3_responses)
 
-    self.assertEquals({}, desc.dir_v2_responses_unknown)
-    self.assertEquals({}, desc.dir_v2_responses_unknown)
+    self.assertEqual({}, desc.dir_v2_responses_unknown)
+    self.assertEqual({}, desc.dir_v2_responses_unknown)
 
   def test_minimal_extrainfo_descriptor(self):
     """
@@ -129,8 +129,8 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
     desc = get_relay_extrainfo_descriptor()
 
-    self.assertEquals('ninja', desc.nickname)
-    self.assertEquals('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
+    self.assertEqual('ninja', desc.nickname)
+    self.assertEqual('B2289C3EAB83ECD6EB916A2F481A02E6B76A0A48', desc.fingerprint)
     self.assertTrue(CRYPTO_BLOB in desc.signature)
 
   def test_unrecognized_line(self):
@@ -139,7 +139,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     """
 
     desc = get_relay_extrainfo_descriptor({'pepperjack': 'is oh so tasty!'})
-    self.assertEquals(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
+    self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
 
   def test_proceeding_line(self):
     """
@@ -174,8 +174,8 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     for entry in test_entries:
       desc_text = get_relay_extrainfo_descriptor({'extra-info': entry}, content = True)
       desc = self._expect_invalid_attr(desc_text, 'nickname')
-      self.assertEquals(None, desc.nickname)
-      self.assertEquals(None, desc.fingerprint)
+      self.assertEqual(None, desc.nickname)
+      self.assertEqual(None, desc.fingerprint)
 
   def test_geoip_db_digest(self):
     """
@@ -185,10 +185,10 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
     geoip_db_digest = '916A3CA8B7DF61473D5AE5B21711F35F301CE9E8'
     desc = get_relay_extrainfo_descriptor({'geoip-db-digest': geoip_db_digest})
-    self.assertEquals(geoip_db_digest, desc.geoip_db_digest)
+    self.assertEqual(geoip_db_digest, desc.geoip_db_digest)
 
     desc = get_relay_extrainfo_descriptor({'geoip6-db-digest': geoip_db_digest})
-    self.assertEquals(geoip_db_digest, desc.geoip6_db_digest)
+    self.assertEqual(geoip_db_digest, desc.geoip6_db_digest)
 
     test_entries = (
       '',
@@ -217,7 +217,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
     for entry in ('0', '11', '25'):
       desc = get_relay_extrainfo_descriptor({'cell-circuits-per-decile': entry})
-      self.assertEquals(int(entry), desc.cell_circuits_per_decile)
+      self.assertEqual(int(entry), desc.cell_circuits_per_decile)
 
     test_entries = (
       '',
@@ -242,11 +242,11 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
       test_value = 'ok=0,unavailable=0,not-found=984,not-modified=0,something-new=7'
       desc = get_relay_extrainfo_descriptor({keyword: test_value})
-      self.assertEquals(0, getattr(desc, attr)[DirResponse.OK])
-      self.assertEquals(0, getattr(desc, attr)[DirResponse.UNAVAILABLE])
-      self.assertEquals(984, getattr(desc, attr)[DirResponse.NOT_FOUND])
-      self.assertEquals(0, getattr(desc, attr)[DirResponse.NOT_MODIFIED])
-      self.assertEquals(7, getattr(desc, unknown_attr)['something-new'])
+      self.assertEqual(0, getattr(desc, attr)[DirResponse.OK])
+      self.assertEqual(0, getattr(desc, attr)[DirResponse.UNAVAILABLE])
+      self.assertEqual(984, getattr(desc, attr)[DirResponse.NOT_FOUND])
+      self.assertEqual(0, getattr(desc, attr)[DirResponse.NOT_MODIFIED])
+      self.assertEqual(7, getattr(desc, unknown_attr)['something-new'])
 
       test_entries = (
         'ok=-4',
@@ -272,23 +272,23 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
       test_value = 'complete=2712,timeout=32,running=4,min=741,d1=14507,d2=22702,q1=28881,d3=38277,d4=73729,md=111455,d6=168231,d7=257218,q3=319833,d8=390507,d9=616301,something-new=11,max=29917857'
       desc = get_relay_extrainfo_descriptor({keyword: test_value})
-      self.assertEquals(2712, getattr(desc, attr)[DirStat.COMPLETE])
-      self.assertEquals(32, getattr(desc, attr)[DirStat.TIMEOUT])
-      self.assertEquals(4, getattr(desc, attr)[DirStat.RUNNING])
-      self.assertEquals(741, getattr(desc, attr)[DirStat.MIN])
-      self.assertEquals(14507, getattr(desc, attr)[DirStat.D1])
-      self.assertEquals(22702, getattr(desc, attr)[DirStat.D2])
-      self.assertEquals(28881, getattr(desc, attr)[DirStat.Q1])
-      self.assertEquals(38277, getattr(desc, attr)[DirStat.D3])
-      self.assertEquals(73729, getattr(desc, attr)[DirStat.D4])
-      self.assertEquals(111455, getattr(desc, attr)[DirStat.MD])
-      self.assertEquals(168231, getattr(desc, attr)[DirStat.D6])
-      self.assertEquals(257218, getattr(desc, attr)[DirStat.D7])
-      self.assertEquals(319833, getattr(desc, attr)[DirStat.Q3])
-      self.assertEquals(390507, getattr(desc, attr)[DirStat.D8])
-      self.assertEquals(616301, getattr(desc, attr)[DirStat.D9])
-      self.assertEquals(29917857, getattr(desc, attr)[DirStat.MAX])
-      self.assertEquals(11, getattr(desc, unknown_attr)['something-new'])
+      self.assertEqual(2712, getattr(desc, attr)[DirStat.COMPLETE])
+      self.assertEqual(32, getattr(desc, attr)[DirStat.TIMEOUT])
+      self.assertEqual(4, getattr(desc, attr)[DirStat.RUNNING])
+      self.assertEqual(741, getattr(desc, attr)[DirStat.MIN])
+      self.assertEqual(14507, getattr(desc, attr)[DirStat.D1])
+      self.assertEqual(22702, getattr(desc, attr)[DirStat.D2])
+      self.assertEqual(28881, getattr(desc, attr)[DirStat.Q1])
+      self.assertEqual(38277, getattr(desc, attr)[DirStat.D3])
+      self.assertEqual(73729, getattr(desc, attr)[DirStat.D4])
+      self.assertEqual(111455, getattr(desc, attr)[DirStat.MD])
+      self.assertEqual(168231, getattr(desc, attr)[DirStat.D6])
+      self.assertEqual(257218, getattr(desc, attr)[DirStat.D7])
+      self.assertEqual(319833, getattr(desc, attr)[DirStat.Q3])
+      self.assertEqual(390507, getattr(desc, attr)[DirStat.D8])
+      self.assertEqual(616301, getattr(desc, attr)[DirStat.D9])
+      self.assertEqual(29917857, getattr(desc, attr)[DirStat.MAX])
+      self.assertEqual(11, getattr(desc, unknown_attr)['something-new'])
 
       test_entries = (
         'complete=-4',
@@ -308,12 +308,12 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     """
 
     desc = get_relay_extrainfo_descriptor({'conn-bi-direct': '2012-05-03 12:07:50 (500 s) 277431,12089,0,2134'})
-    self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), desc.conn_bi_direct_end)
-    self.assertEquals(500, desc.conn_bi_direct_interval)
-    self.assertEquals(277431, desc.conn_bi_direct_below)
-    self.assertEquals(12089, desc.conn_bi_direct_read)
-    self.assertEquals(0, desc.conn_bi_direct_write)
-    self.assertEquals(2134, desc.conn_bi_direct_both)
+    self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), desc.conn_bi_direct_end)
+    self.assertEqual(500, desc.conn_bi_direct_interval)
+    self.assertEqual(277431, desc.conn_bi_direct_below)
+    self.assertEqual(12089, desc.conn_bi_direct_read)
+    self.assertEqual(0, desc.conn_bi_direct_write)
+    self.assertEqual(2134, desc.conn_bi_direct_both)
 
     test_entries = (
       '',
@@ -332,12 +332,12 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     for entry in test_entries:
       desc_text = get_relay_extrainfo_descriptor({'conn-bi-direct': entry}, content = True)
       desc = self._expect_invalid_attr(desc_text)
-      self.assertEquals(None, desc.conn_bi_direct_end)
-      self.assertEquals(None, desc.conn_bi_direct_interval)
-      self.assertEquals(None, desc.conn_bi_direct_below)
-      self.assertEquals(None, desc.conn_bi_direct_read)
-      self.assertEquals(None, desc.conn_bi_direct_write)
-      self.assertEquals(None, desc.conn_bi_direct_both)
+      self.assertEqual(None, desc.conn_bi_direct_end)
+      self.assertEqual(None, desc.conn_bi_direct_interval)
+      self.assertEqual(None, desc.conn_bi_direct_below)
+      self.assertEqual(None, desc.conn_bi_direct_read)
+      self.assertEqual(None, desc.conn_bi_direct_write)
+      self.assertEqual(None, desc.conn_bi_direct_both)
 
   def test_percentage_lines(self):
     """
@@ -357,7 +357,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
       for test_value, expected_value in test_entries:
         desc = get_relay_extrainfo_descriptor({keyword: test_value})
-        self.assertEquals(expected_value, getattr(desc, attr))
+        self.assertEqual(expected_value, getattr(desc, attr))
 
       test_entries = (
         ('', None),
@@ -388,7 +388,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
       for test_value, expected_value in test_entries:
         desc = get_relay_extrainfo_descriptor({keyword: test_value})
-        self.assertEquals(expected_value, getattr(desc, attr))
+        self.assertEqual(expected_value, getattr(desc, attr))
 
       test_entries = (
         (',,11', [11.0]),
@@ -410,7 +410,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
       attr = keyword.replace('-', '_')
 
       desc = get_relay_extrainfo_descriptor({keyword: '2012-05-03 12:07:50'})
-      self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, attr))
+      self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, attr))
 
       test_entries = (
         '',
@@ -434,8 +434,8 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
       interval_attr = end_attr[:-4] + '_interval'
 
       desc = get_relay_extrainfo_descriptor({keyword: '2012-05-03 12:07:50 (500 s)'})
-      self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
-      self.assertEquals(500, getattr(desc, interval_attr))
+      self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
+      self.assertEqual(500, getattr(desc, interval_attr))
 
       test_entries = (
         '',
@@ -450,8 +450,8 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
       for entry in test_entries:
         desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
         desc = self._expect_invalid_attr(desc_text)
-        self.assertEquals(None, getattr(desc, end_attr))
-        self.assertEquals(None, getattr(desc, interval_attr))
+        self.assertEqual(None, getattr(desc, end_attr))
+        self.assertEqual(None, getattr(desc, interval_attr))
 
   def test_timestamp_interval_and_value_lines(self):
     """
@@ -473,9 +473,9 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
       for test_values, expected_values in test_entries:
         desc = get_relay_extrainfo_descriptor({keyword: '2012-05-03 12:07:50 (500 s)%s' % test_values})
-        self.assertEquals(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
-        self.assertEquals(500, getattr(desc, interval_attr))
-        self.assertEquals(expected_values, getattr(desc, values_attr))
+        self.assertEqual(datetime.datetime(2012, 5, 3, 12, 7, 50), getattr(desc, end_attr))
+        self.assertEqual(500, getattr(desc, interval_attr))
+        self.assertEqual(expected_values, getattr(desc, values_attr))
 
       test_entries = (
         '',
@@ -491,9 +491,9 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
       for entry in test_entries:
         desc_text = get_relay_extrainfo_descriptor({keyword: entry}, content = True)
         desc = self._expect_invalid_attr(desc_text)
-        self.assertEquals(None, getattr(desc, end_attr))
-        self.assertEquals(None, getattr(desc, interval_attr))
-        self.assertEquals(None, getattr(desc, values_attr))
+        self.assertEqual(None, getattr(desc, end_attr))
+        self.assertEqual(None, getattr(desc, interval_attr))
+        self.assertEqual(None, getattr(desc, values_attr))
 
   def test_port_mapping_lines(self):
     """
@@ -512,7 +512,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
       for test_value, expected_value in test_entries:
         desc = get_relay_extrainfo_descriptor({keyword: test_value})
-        self.assertEquals(expected_value, getattr(desc, attr))
+        self.assertEqual(expected_value, getattr(desc, attr))
 
       test_entries = (
         '8000000=115533759',
@@ -543,7 +543,7 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
       for test_value, expected_value in test_entries:
         desc = get_relay_extrainfo_descriptor({keyword: test_value})
-        self.assertEquals(expected_value, getattr(desc, attr))
+        self.assertEqual(expected_value, getattr(desc, attr))
 
       test_entries = (
         'uk=-4',
@@ -563,10 +563,10 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
 
     desc = get_bridge_extrainfo_descriptor()
 
-    self.assertEquals('ec2bridgereaac65a3', desc.nickname)
-    self.assertEquals('1EC248422B57D9C0BD751892FE787585407479A4', desc.fingerprint)
-    self.assertEquals('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual('ec2bridgereaac65a3', desc.nickname)
+    self.assertEqual('1EC248422B57D9C0BD751892FE787585407479A4', desc.fingerprint)
+    self.assertEqual('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
+    self.assertEqual([], desc.get_unrecognized_lines())
 
     # check that we don't have crypto fields
     self.assertRaises(AttributeError, getattr, desc, 'signature')
@@ -577,10 +577,10 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     """
 
     desc = get_bridge_extrainfo_descriptor({'bridge-ip-versions': 'v4=16,v6=40'})
-    self.assertEquals({'v4': 16, 'v6': 40}, desc.ip_versions)
+    self.assertEqual({'v4': 16, 'v6': 40}, desc.ip_versions)
 
     desc = get_bridge_extrainfo_descriptor({'bridge-ip-versions': ''})
-    self.assertEquals({}, desc.ip_versions)
+    self.assertEqual({}, desc.ip_versions)
 
     desc_text = get_bridge_extrainfo_descriptor({'bridge-ip-versions': 'v4=24.5'}, content = True)
     self.assertRaises(ValueError, RelayExtraInfoDescriptor, desc_text)
@@ -591,10 +591,10 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     """
 
     desc = get_bridge_extrainfo_descriptor({'bridge-ip-transports': '<OR>=16,<??>=40'})
-    self.assertEquals({'<OR>': 16, '<??>': 40}, desc.ip_transports)
+    self.assertEqual({'<OR>': 16, '<??>': 40}, desc.ip_transports)
 
     desc = get_bridge_extrainfo_descriptor({'bridge-ip-transports': ''})
-    self.assertEquals({}, desc.ip_transports)
+    self.assertEqual({}, desc.ip_transports)
 
     desc_text = get_bridge_extrainfo_descriptor({'bridge-ip-transports': '<OR>=24.5'}, content = True)
     self.assertRaises(ValueError, RelayExtraInfoDescriptor, desc_text)
@@ -605,17 +605,17 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
     """
 
     desc = get_bridge_extrainfo_descriptor({'transport': 'obfs3'})
-    self.assertEquals({'obfs3': (None, None, None)}, desc.transport)
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual({'obfs3': (None, None, None)}, desc.transport)
+    self.assertEqual([], desc.get_unrecognized_lines())
 
     desc = get_relay_extrainfo_descriptor({'transport': 'obfs2 83.212.96.201:33570'})
-    self.assertEquals({'obfs2': ('83.212.96.201', 33570, [])}, desc.transport)
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual({'obfs2': ('83.212.96.201', 33570, [])}, desc.transport)
+    self.assertEqual([], desc.get_unrecognized_lines())
 
     # multiple transport lines
     desc = get_bridge_extrainfo_descriptor({'transport': 'obfs3\ntransport obfs4'})
-    self.assertEquals({'obfs3': (None, None, None), 'obfs4': (None, None, None)}, desc.transport)
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual({'obfs3': (None, None, None), 'obfs4': (None, None, None)}, desc.transport)
+    self.assertEqual([], desc.get_unrecognized_lines())
 
   def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
     """
@@ -631,9 +631,9 @@ k0d2aofcVbHr4fPQOSST0LXDrhFl5Fqo5um296zpJGvRUeO6S44U/EfJAGShtqWw
       # check that the invalid attribute matches the expected value when
       # constructed without validation
 
-      self.assertEquals(expected_value, getattr(desc, attr))
+      self.assertEqual(expected_value, getattr(desc, attr))
     else:
       # check a default attribute
-      self.assertEquals('ninja', desc.nickname)
+      self.assertEqual('ninja', desc.nickname)
 
     return desc
diff --git a/test/unit/descriptor/microdescriptor.py b/test/unit/descriptor/microdescriptor.py
index 267b01a..5635d54 100644
--- a/test/unit/descriptor/microdescriptor.py
+++ b/test/unit/descriptor/microdescriptor.py
@@ -54,31 +54,31 @@ class TestMicrodescriptor(unittest.TestCase):
       descriptors = stem.descriptor.parse_file(descriptor_file, 'microdescriptor 1.0')
 
       router = next(descriptors)
-      self.assertEquals(FIRST_ONION_KEY, router.onion_key)
-      self.assertEquals(None, router.ntor_onion_key)
-      self.assertEquals([], router.or_addresses)
-      self.assertEquals([], router.family)
-      self.assertEquals(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
-      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
-      self.assertEquals([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
+      self.assertEqual(FIRST_ONION_KEY, router.onion_key)
+      self.assertEqual(None, router.ntor_onion_key)
+      self.assertEqual([], router.or_addresses)
+      self.assertEqual([], router.family)
+      self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
+      self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
+      self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
 
       router = next(descriptors)
-      self.assertEquals(SECOND_ONION_KEY, router.onion_key)
-      self.assertEquals(u'r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k=', router.ntor_onion_key)
-      self.assertEquals([], router.or_addresses)
-      self.assertEquals(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'], router.family)
-      self.assertEquals(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
-      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:37'}, router.get_annotations())
-      self.assertEquals([b'@last-listed 2013-02-24 00:18:37'], router.get_annotation_lines())
+      self.assertEqual(SECOND_ONION_KEY, router.onion_key)
+      self.assertEqual(u'r5572HzD+PMPBbXlZwBhsm6YEbxnYgis8vhZ1jmdI2k=', router.ntor_onion_key)
+      self.assertEqual([], router.or_addresses)
+      self.assertEqual(['$6141629FA0D15A6AEAEF3A1BEB76E64C767B3174'], router.family)
+      self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), router.exit_policy)
+      self.assertEqual({b'@last-listed': b'2013-02-24 00:18:37'}, router.get_annotations())
+      self.assertEqual([b'@last-listed 2013-02-24 00:18:37'], router.get_annotation_lines())
 
       router = next(descriptors)
-      self.assertEquals(THIRD_ONION_KEY, router.onion_key)
-      self.assertEquals(None, router.ntor_onion_key)
-      self.assertEquals([(u'2001:6b0:7:125::242', 9001, True)], router.or_addresses)
-      self.assertEquals([], router.family)
-      self.assertEquals(stem.exit_policy.MicroExitPolicy('accept 80,443'), router.exit_policy)
-      self.assertEquals({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
-      self.assertEquals([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
+      self.assertEqual(THIRD_ONION_KEY, router.onion_key)
+      self.assertEqual(None, router.ntor_onion_key)
+      self.assertEqual([(u'2001:6b0:7:125::242', 9001, True)], router.or_addresses)
+      self.assertEqual([], router.family)
+      self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 80,443'), router.exit_policy)
+      self.assertEqual({b'@last-listed': b'2013-02-24 00:18:36'}, router.get_annotations())
+      self.assertEqual([b'@last-listed 2013-02-24 00:18:36'], router.get_annotation_lines())
 
   def test_minimal_microdescriptor(self):
     """
@@ -89,14 +89,14 @@ class TestMicrodescriptor(unittest.TestCase):
     desc = get_microdescriptor()
 
     self.assertTrue(CRYPTO_BLOB in desc.onion_key)
-    self.assertEquals(None, desc.ntor_onion_key)
-    self.assertEquals([], desc.or_addresses)
-    self.assertEquals([], desc.family)
-    self.assertEquals(stem.exit_policy.MicroExitPolicy('reject 1-65535'), desc.exit_policy)
-    self.assertEquals(None, desc.exit_policy_v6)
-    self.assertEquals(None, desc.identifier_type)
-    self.assertEquals(None, desc.identifier)
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual(None, desc.ntor_onion_key)
+    self.assertEqual([], desc.or_addresses)
+    self.assertEqual([], desc.family)
+    self.assertEqual(stem.exit_policy.MicroExitPolicy('reject 1-65535'), desc.exit_policy)
+    self.assertEqual(None, desc.exit_policy_v6)
+    self.assertEqual(None, desc.identifier_type)
+    self.assertEqual(None, desc.identifier)
+    self.assertEqual([], desc.get_unrecognized_lines())
 
   def test_unrecognized_line(self):
     """
@@ -104,7 +104,7 @@ class TestMicrodescriptor(unittest.TestCase):
     """
 
     desc = get_microdescriptor({'pepperjack': 'is oh so tasty!'})
-    self.assertEquals(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
+    self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
 
   def test_proceeding_line(self):
     """
@@ -115,7 +115,7 @@ class TestMicrodescriptor(unittest.TestCase):
     self.assertRaises(ValueError, Microdescriptor, desc_text)
 
     desc = Microdescriptor(desc_text, validate = False)
-    self.assertEquals(['Amunet1'], desc.family)
+    self.assertEqual(['Amunet1'], desc.family)
 
   def test_a_line(self):
     """
@@ -132,7 +132,7 @@ class TestMicrodescriptor(unittest.TestCase):
     ]
 
     desc = Microdescriptor(desc_text)
-    self.assertEquals(expected, desc.or_addresses)
+    self.assertEqual(expected, desc.or_addresses)
 
   def test_family(self):
     """
@@ -140,7 +140,7 @@ class TestMicrodescriptor(unittest.TestCase):
     """
 
     desc = get_microdescriptor({'family': 'Amunet1 Amunet2 Amunet3'})
-    self.assertEquals(['Amunet1', 'Amunet2', 'Amunet3'], desc.family)
+    self.assertEqual(['Amunet1', 'Amunet2', 'Amunet3'], desc.family)
 
     # try multiple family lines
 
@@ -152,7 +152,7 @@ class TestMicrodescriptor(unittest.TestCase):
 
     # family entries will overwrite each other
     desc = Microdescriptor(desc_text, validate = False)
-    self.assertEquals(1, len(desc.family))
+    self.assertEqual(1, len(desc.family))
 
   def test_exit_policy(self):
     """
@@ -161,7 +161,7 @@ class TestMicrodescriptor(unittest.TestCase):
     """
 
     desc = get_microdescriptor({'p': 'accept 80,110,143,443'})
-    self.assertEquals(stem.exit_policy.MicroExitPolicy('accept 80,110,143,443'), desc.exit_policy)
+    self.assertEqual(stem.exit_policy.MicroExitPolicy('accept 80,110,143,443'), desc.exit_policy)
 
   def test_identifier(self):
     """
@@ -169,5 +169,5 @@ class TestMicrodescriptor(unittest.TestCase):
     """
 
     desc = get_microdescriptor({'id': 'rsa1024 Cd47okjCHD83YGzThGBDptXs9Z4'})
-    self.assertEquals('rsa1024', desc.identifier_type)
-    self.assertEquals('Cd47okjCHD83YGzThGBDptXs9Z4', desc.identifier)
+    self.assertEqual('rsa1024', desc.identifier_type)
+    self.assertEqual('Cd47okjCHD83YGzThGBDptXs9Z4', desc.identifier)
diff --git a/test/unit/descriptor/networkstatus/bridge_document.py b/test/unit/descriptor/networkstatus/bridge_document.py
index a45e802..2ec2a22 100644
--- a/test/unit/descriptor/networkstatus/bridge_document.py
+++ b/test/unit/descriptor/networkstatus/bridge_document.py
@@ -34,13 +34,13 @@ class TestBridgeNetworkStatusDocument(unittest.TestCase):
 
     with open(consensus_path, 'rb') as descriptor_file:
       router = next(stem.descriptor.parse_file(descriptor_file))
-      self.assertEquals('Unnamed', router.nickname)
-      self.assertEquals('0014A2055278DB3EB0E59EA701741416AF185558', router.fingerprint)
-      self.assertEquals('148EF8685B8D259650AE0967D1FF8E6A870C7743', router.digest)
-      self.assertEquals(datetime.datetime(2012, 5, 31, 15, 57, 0), router.published)
-      self.assertEquals('10.97.236.247', router.address)
-      self.assertEquals(443, router.or_port)
-      self.assertEquals(None, router.dir_port)
+      self.assertEqual('Unnamed', router.nickname)
+      self.assertEqual('0014A2055278DB3EB0E59EA701741416AF185558', router.fingerprint)
+      self.assertEqual('148EF8685B8D259650AE0967D1FF8E6A870C7743', router.digest)
+      self.assertEqual(datetime.datetime(2012, 5, 31, 15, 57, 0), router.published)
+      self.assertEqual('10.97.236.247', router.address)
+      self.assertEqual(443, router.or_port)
+      self.assertEqual(None, router.dir_port)
 
   def test_metrics_cert(self):
     """
@@ -80,17 +80,17 @@ GM9hAsAMRX9Ogqhq5UjDNqEsvDKuyVeyh7unSZEOip9Zr6K/+7VsVPNb8vfBRBjo
 
     with open(cert_path, 'rb') as cert_file:
       cert = next(stem.descriptor.parse_file(cert_file))
-      self.assertEquals(3, cert.version)
-      self.assertEquals(None, cert.address)
-      self.assertEquals(None, cert.dir_port)
-      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', cert.fingerprint)
-      self.assertEquals(expected_identity_key, cert.identity_key)
-      self.assertEquals(datetime.datetime(2008, 5, 9, 21, 13, 26), cert.published)
-      self.assertEquals(datetime.datetime(2009, 5, 9, 21, 13, 26), cert.expires)
-      self.assertEquals(expected_signing_key, cert.signing_key)
-      self.assertEquals(None, cert.crosscert)
-      self.assertEquals(expected_key_cert, cert.certification)
-      self.assertEquals([], cert.get_unrecognized_lines())
+      self.assertEqual(3, cert.version)
+      self.assertEqual(None, cert.address)
+      self.assertEqual(None, cert.dir_port)
+      self.assertEqual('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', cert.fingerprint)
+      self.assertEqual(expected_identity_key, cert.identity_key)
+      self.assertEqual(datetime.datetime(2008, 5, 9, 21, 13, 26), cert.published)
+      self.assertEqual(datetime.datetime(2009, 5, 9, 21, 13, 26), cert.expires)
+      self.assertEqual(expected_signing_key, cert.signing_key)
+      self.assertEqual(None, cert.crosscert)
+      self.assertEqual(expected_key_cert, cert.certification)
+      self.assertEqual([], cert.get_unrecognized_lines())
 
   def test_empty_document(self):
     """
@@ -111,5 +111,5 @@ GM9hAsAMRX9Ogqhq5UjDNqEsvDKuyVeyh7unSZEOip9Zr6K/+7VsVPNb8vfBRBjo
     self.assertEqual(datetime.datetime(2012, 6, 1, 4, 7, 4), document.published)
 
     self.assertEqual(2, len(document.routers))
-    self.assertEqual(set(['Unnamed', 'TolFuin']), set([desc.nickname for desc in document.routers.values()]))
+    self.assertEqual(set(['Unnamed', 'TolFuin']), set([desc.nickname for desc in list(document.routers.values())]))
     self.assertEqual([], document.get_unrecognized_lines())
diff --git a/test/unit/descriptor/networkstatus/directory_authority.py b/test/unit/descriptor/networkstatus/directory_authority.py
index dd769f7..1114518 100644
--- a/test/unit/descriptor/networkstatus/directory_authority.py
+++ b/test/unit/descriptor/networkstatus/directory_authority.py
@@ -55,7 +55,7 @@ class TestDirectoryAuthority(unittest.TestCase):
     """
 
     authority = get_directory_authority({'pepperjack': 'is oh so tasty!'})
-    self.assertEquals(['pepperjack is oh so tasty!'], authority.get_unrecognized_lines())
+    self.assertEqual(['pepperjack is oh so tasty!'], authority.get_unrecognized_lines())
 
   def test_legacy_authority(self):
     """
diff --git a/test/unit/descriptor/networkstatus/document_v2.py b/test/unit/descriptor/networkstatus/document_v2.py
index 7ecea87..54cbccc 100644
--- a/test/unit/descriptor/networkstatus/document_v2.py
+++ b/test/unit/descriptor/networkstatus/document_v2.py
@@ -34,59 +34,59 @@ TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E=
       descriptor_file.readline()  # strip header
       document = stem.descriptor.networkstatus.NetworkStatusDocumentV2(descriptor_file.read())
 
-      self.assertEquals(2, document.version)
-      self.assertEquals('18.244.0.114', document.hostname)
-      self.assertEquals('18.244.0.114', document.address)
-      self.assertEquals(80, document.dir_port)
-      self.assertEquals('719BE45DE224B607C53707D0E2143E2D423E74CF', document.fingerprint)
-      self.assertEquals('arma at mit dot edu', document.contact)
-      self.assertEquals(expected_signing_key, document.signing_key)
-
-      self.assertEquals(67, len(document.client_versions))
-      self.assertEquals('0.0.9rc2', document.client_versions[0])
-      self.assertEquals('0.1.1.10-alpha-cvs', document.client_versions[-1])
-
-      self.assertEquals(67, len(document.server_versions))
-      self.assertEquals('0.0.9rc2', document.server_versions[0])
-      self.assertEquals('0.1.1.10-alpha-cvs', document.server_versions[-1])
-
-      self.assertEquals(datetime.datetime(2005, 12, 16, 0, 13, 46), document.published)
-      self.assertEquals(['Names', 'Versions'], document.options)
-      self.assertEquals('moria2', document.signing_authority)
-      self.assertEquals(expected_signature, document.signature)
-      self.assertEquals([], document.get_unrecognized_lines())
+      self.assertEqual(2, document.version)
+      self.assertEqual('18.244.0.114', document.hostname)
+      self.assertEqual('18.244.0.114', document.address)
+      self.assertEqual(80, document.dir_port)
+      self.assertEqual('719BE45DE224B607C53707D0E2143E2D423E74CF', document.fingerprint)
+      self.assertEqual('arma at mit dot edu', document.contact)
+      self.assertEqual(expected_signing_key, document.signing_key)
+
+      self.assertEqual(67, len(document.client_versions))
+      self.assertEqual('0.0.9rc2', document.client_versions[0])
+      self.assertEqual('0.1.1.10-alpha-cvs', document.client_versions[-1])
+
+      self.assertEqual(67, len(document.server_versions))
+      self.assertEqual('0.0.9rc2', document.server_versions[0])
+      self.assertEqual('0.1.1.10-alpha-cvs', document.server_versions[-1])
+
+      self.assertEqual(datetime.datetime(2005, 12, 16, 0, 13, 46), document.published)
+      self.assertEqual(['Names', 'Versions'], document.options)
+      self.assertEqual('moria2', document.signing_authority)
+      self.assertEqual(expected_signature, document.signature)
+      self.assertEqual([], document.get_unrecognized_lines())
 
       self.assertEqual(3, len(document.routers))
 
       router1 = document.routers['719BE45DE224B607C53707D0E2143E2D423E74CF']
-      self.assertEquals('moria2', router1.nickname)
-      self.assertEquals('719BE45DE224B607C53707D0E2143E2D423E74CF', router1.fingerprint)
-      self.assertEquals('B7F3F0975B87889DD1285FD57A1B1BB617F65432', router1.digest)
-      self.assertEquals(datetime.datetime(2005, 12, 15, 6, 57, 18), router1.published)
-      self.assertEquals('18.244.0.114', router1.address)
-      self.assertEquals(443, router1.or_port)
-      self.assertEquals(80, router1.dir_port)
-      self.assertEquals(set(['Authority', 'Fast', 'Named', 'Running', 'Valid', 'V2Dir']), set(router1.flags))
+      self.assertEqual('moria2', router1.nickname)
+      self.assertEqual('719BE45DE224B607C53707D0E2143E2D423E74CF', router1.fingerprint)
+      self.assertEqual('B7F3F0975B87889DD1285FD57A1B1BB617F65432', router1.digest)
+      self.assertEqual(datetime.datetime(2005, 12, 15, 6, 57, 18), router1.published)
+      self.assertEqual('18.244.0.114', router1.address)
+      self.assertEqual(443, router1.or_port)
+      self.assertEqual(80, router1.dir_port)
+      self.assertEqual(set(['Authority', 'Fast', 'Named', 'Running', 'Valid', 'V2Dir']), set(router1.flags))
 
       router2 = document.routers['0928BA467056C4A689FEE4EF5D71482B6289C3D5']
-      self.assertEquals('stnv', router2.nickname)
-      self.assertEquals('0928BA467056C4A689FEE4EF5D71482B6289C3D5', router2.fingerprint)
-      self.assertEquals('22D1A7ED4199BDA7ED6C416EECD769C18E1F2A5A', router2.digest)
-      self.assertEquals(datetime.datetime(2005, 12, 15, 16, 24, 42), router2.published)
-      self.assertEquals('84.16.236.173', router2.address)
-      self.assertEquals(9001, router2.or_port)
-      self.assertEquals(None, router2.dir_port)
-      self.assertEquals(set(['Named', 'Valid']), set(router2.flags))
+      self.assertEqual('stnv', router2.nickname)
+      self.assertEqual('0928BA467056C4A689FEE4EF5D71482B6289C3D5', router2.fingerprint)
+      self.assertEqual('22D1A7ED4199BDA7ED6C416EECD769C18E1F2A5A', router2.digest)
+      self.assertEqual(datetime.datetime(2005, 12, 15, 16, 24, 42), router2.published)
+      self.assertEqual('84.16.236.173', router2.address)
+      self.assertEqual(9001, router2.or_port)
+      self.assertEqual(None, router2.dir_port)
+      self.assertEqual(set(['Named', 'Valid']), set(router2.flags))
 
       router3 = document.routers['09E8582FF0E6F85E2B8E41C0DC0B9C9DC46E6968']
-      self.assertEquals('nggrplz', router3.nickname)
-      self.assertEquals('09E8582FF0E6F85E2B8E41C0DC0B9C9DC46E6968', router3.fingerprint)
-      self.assertEquals('B302C2B01C94F398E3EF38939526B0651F824DD6', router3.digest)
-      self.assertEquals(datetime.datetime(2005, 12, 15, 23, 25, 50), router3.published)
-      self.assertEquals('194.109.109.109', router3.address)
-      self.assertEquals(9001, router3.or_port)
-      self.assertEquals(None, router3.dir_port)
-      self.assertEquals(set(['Fast', 'Stable', 'Running', 'Valid']), set(router3.flags))
+      self.assertEqual('nggrplz', router3.nickname)
+      self.assertEqual('09E8582FF0E6F85E2B8E41C0DC0B9C9DC46E6968', router3.fingerprint)
+      self.assertEqual('B302C2B01C94F398E3EF38939526B0651F824DD6', router3.digest)
+      self.assertEqual(datetime.datetime(2005, 12, 15, 23, 25, 50), router3.published)
+      self.assertEqual('194.109.109.109', router3.address)
+      self.assertEqual(9001, router3.or_port)
+      self.assertEqual(None, router3.dir_port)
+      self.assertEqual(set(['Fast', 'Stable', 'Running', 'Valid']), set(router3.flags))
 
   def test_minimal_document(self):
     """
@@ -95,17 +95,17 @@ TpQQk3nNQF8z6UIvdlvP+DnJV4izWVkQEZgUZgIVM0E=
 
     document = get_network_status_document_v2()
 
-    self.assertEquals({}, document.routers)
-    self.assertEquals(2, document.version)
-    self.assertEquals('18.244.0.114', document.hostname)
-    self.assertEquals('18.244.0.114', document.address)
-    self.assertEquals(80, document.dir_port)
-    self.assertEquals('719BE45DE224B607C53707D0E2143E2D423E74CF', document.fingerprint)
-    self.assertEquals('arma at mit dot edu', document.contact)
-    self.assertEquals(NETWORK_STATUS_DOCUMENT_HEADER_V2[5][1][1:], document.signing_key)
-    self.assertEquals([], document.client_versions)
-    self.assertEquals([], document.server_versions)
-    self.assertEquals(datetime.datetime(2005, 12, 16, 0, 13, 46), document.published)
-    self.assertEquals([], document.options)
-    self.assertEquals('moria2', document.signing_authority)
-    self.assertEquals(NETWORK_STATUS_DOCUMENT_FOOTER_V2[0][1][7:], document.signature)
+    self.assertEqual({}, document.routers)
+    self.assertEqual(2, document.version)
+    self.assertEqual('18.244.0.114', document.hostname)
+    self.assertEqual('18.244.0.114', document.address)
+    self.assertEqual(80, document.dir_port)
+    self.assertEqual('719BE45DE224B607C53707D0E2143E2D423E74CF', document.fingerprint)
+    self.assertEqual('arma at mit dot edu', document.contact)
+    self.assertEqual(NETWORK_STATUS_DOCUMENT_HEADER_V2[5][1][1:], document.signing_key)
+    self.assertEqual([], document.client_versions)
+    self.assertEqual([], document.server_versions)
+    self.assertEqual(datetime.datetime(2005, 12, 16, 0, 13, 46), document.published)
+    self.assertEqual([], document.options)
+    self.assertEqual('moria2', document.signing_authority)
+    self.assertEqual(NETWORK_STATUS_DOCUMENT_FOOTER_V2[0][1][7:], document.signature)
diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py
index 261a70f..63a72f0 100644
--- a/test/unit/descriptor/networkstatus/document_v3.py
+++ b/test/unit/descriptor/networkstatus/document_v3.py
@@ -62,13 +62,13 @@ class TestNetworkStatusDocument(unittest.TestCase):
           descriptors = stem.descriptor.parse_file(descriptor_file)
 
         router = next(descriptors)
-        self.assertEquals('sumkledi', router.nickname)
-        self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-        self.assertEquals('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
-        self.assertEquals(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
-        self.assertEquals('178.218.213.229', router.address)
-        self.assertEquals(80, router.or_port)
-        self.assertEquals(None, router.dir_port)
+        self.assertEqual('sumkledi', router.nickname)
+        self.assertEqual('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+        self.assertEqual('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
+        self.assertEqual(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
+        self.assertEqual('178.218.213.229', router.address)
+        self.assertEqual(80, router.or_port)
+        self.assertEqual(None, router.dir_port)
 
   def test_consensus_v3(self):
     """
@@ -112,56 +112,56 @@ I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
     with open(get_resource('cached-consensus'), 'rb') as descriptor_file:
       document = stem.descriptor.networkstatus.NetworkStatusDocumentV3(descriptor_file.read(), default_params = False)
 
-      self.assertEquals(3, document.version)
-      self.assertEquals(None, document.version_flavor)
-      self.assertEquals(True, document.is_consensus)
-      self.assertEquals(False, document.is_vote)
-      self.assertEquals(False, document.is_microdescriptor)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 10, 0, 0), document.valid_after)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 11, 0, 0), document.fresh_until)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 13, 0, 0), document.valid_until)
-      self.assertEquals(300, document.vote_delay)
-      self.assertEquals(300, document.dist_delay)
-      self.assertEquals(expected_versions, document.client_versions)
-      self.assertEquals(expected_versions, document.server_versions)
-      self.assertEquals(expected_flags, set(document.known_flags))
-      self.assertEquals({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
-
-      self.assertEquals(12, document.consensus_method)
-      self.assertEquals(expected_bandwidth_weights, document.bandwidth_weights)
-      self.assertEquals([], document.consensus_methods)
-      self.assertEquals(None, document.published)
-      self.assertEquals([], document.get_unrecognized_lines())
+      self.assertEqual(3, document.version)
+      self.assertEqual(None, document.version_flavor)
+      self.assertEqual(True, document.is_consensus)
+      self.assertEqual(False, document.is_vote)
+      self.assertEqual(False, document.is_microdescriptor)
+      self.assertEqual(datetime.datetime(2012, 7, 12, 10, 0, 0), document.valid_after)
+      self.assertEqual(datetime.datetime(2012, 7, 12, 11, 0, 0), document.fresh_until)
+      self.assertEqual(datetime.datetime(2012, 7, 12, 13, 0, 0), document.valid_until)
+      self.assertEqual(300, document.vote_delay)
+      self.assertEqual(300, document.dist_delay)
+      self.assertEqual(expected_versions, document.client_versions)
+      self.assertEqual(expected_versions, document.server_versions)
+      self.assertEqual(expected_flags, set(document.known_flags))
+      self.assertEqual({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
+
+      self.assertEqual(12, document.consensus_method)
+      self.assertEqual(expected_bandwidth_weights, document.bandwidth_weights)
+      self.assertEqual([], document.consensus_methods)
+      self.assertEqual(None, document.published)
+      self.assertEqual([], document.get_unrecognized_lines())
 
       router = document.routers['0013D22389CD50D0B784A3E4061CB31E8CE8CEB5']
-      self.assertEquals('sumkledi', router.nickname)
-      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-      self.assertEquals('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
-      self.assertEquals('178.218.213.229', router.address)
-      self.assertEquals(80, router.or_port)
-      self.assertEquals(None, router.dir_port)
-      self.assertEquals(set(['Exit', 'Fast', 'Named', 'Running', 'Valid']), set(router.flags))
+      self.assertEqual('sumkledi', router.nickname)
+      self.assertEqual('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+      self.assertEqual('F260ABF1297B445E04354E236F4159140FF7768F', router.digest)
+      self.assertEqual(datetime.datetime(2012, 7, 12, 4, 1, 55), router.published)
+      self.assertEqual('178.218.213.229', router.address)
+      self.assertEqual(80, router.or_port)
+      self.assertEqual(None, router.dir_port)
+      self.assertEqual(set(['Exit', 'Fast', 'Named', 'Running', 'Valid']), set(router.flags))
 
       authority = document.directory_authorities[0]
-      self.assertEquals(8, len(document.directory_authorities))
-      self.assertEquals('tor26', authority.nickname)
-      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', authority.fingerprint)
-      self.assertEquals('86.59.21.38', authority.hostname)
-      self.assertEquals('86.59.21.38', authority.address)
-      self.assertEquals(80, authority.dir_port)
-      self.assertEquals(443, authority.or_port)
-      self.assertEquals('Peter Palfrader', authority.contact)
-      self.assertEquals('0B6D1E9A300B895AA2D0B427F92917B6995C3C1C', authority.vote_digest)
-      self.assertEquals(None, authority.legacy_dir_key)
-      self.assertEquals(None, authority.key_certificate)
+      self.assertEqual(8, len(document.directory_authorities))
+      self.assertEqual('tor26', authority.nickname)
+      self.assertEqual('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', authority.fingerprint)
+      self.assertEqual('86.59.21.38', authority.hostname)
+      self.assertEqual('86.59.21.38', authority.address)
+      self.assertEqual(80, authority.dir_port)
+      self.assertEqual(443, authority.or_port)
+      self.assertEqual('Peter Palfrader', authority.contact)
+      self.assertEqual('0B6D1E9A300B895AA2D0B427F92917B6995C3C1C', authority.vote_digest)
+      self.assertEqual(None, authority.legacy_dir_key)
+      self.assertEqual(None, authority.key_certificate)
 
       signature = document.signatures[0]
-      self.assertEquals(8, len(document.signatures))
-      self.assertEquals('sha1', signature.method)
-      self.assertEquals('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', signature.identity)
-      self.assertEquals('BF112F1C6D5543CFD0A32215ACABD4197B5279AD', signature.key_digest)
-      self.assertEquals(expected_signature, signature.signature)
+      self.assertEqual(8, len(document.signatures))
+      self.assertEqual('sha1', signature.method)
+      self.assertEqual('14C131DFC5C6F93646BE72FA1401C02A8DF2E8B4', signature.identity)
+      self.assertEqual('BF112F1C6D5543CFD0A32215ACABD4197B5279AD', signature.key_digest)
+      self.assertEqual(expected_signature, signature.signature)
 
   def test_metrics_vote(self):
     """
@@ -174,13 +174,13 @@ I/TJmV928na7RLZe2mGHCAW3VQOvV+QkCfj05VZ8CsY=
       descriptors = stem.descriptor.parse_file(descriptor_file)
 
       router = next(descriptors)
-      self.assertEquals('sumkledi', router.nickname)
-      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-      self.assertEquals('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
-      self.assertEquals(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
-      self.assertEquals('178.218.213.229', router.address)
-      self.assertEquals(80, router.or_port)
-      self.assertEquals(None, router.dir_port)
+      self.assertEqual('sumkledi', router.nickname)
+      self.assertEqual('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+      self.assertEqual('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
+      self.assertEqual(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
+      self.assertEqual('178.218.213.229', router.address)
+      self.assertEqual(80, router.or_port)
+      self.assertEqual(None, router.dir_port)
 
   def test_vote(self):
     """
@@ -235,66 +235,66 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     with open(get_resource('vote'), 'rb') as descriptor_file:
       document = stem.descriptor.networkstatus.NetworkStatusDocumentV3(descriptor_file.read(), default_params = False)
 
-      self.assertEquals(3, document.version)
-      self.assertEquals(None, document.version_flavor)
-      self.assertEquals(False, document.is_consensus)
-      self.assertEquals(True, document.is_vote)
-      self.assertEquals(False, document.is_microdescriptor)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 0, 0, 0), document.valid_after)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 1, 0, 0), document.fresh_until)
-      self.assertEquals(datetime.datetime(2012, 7, 12, 3, 0, 0), document.valid_until)
-      self.assertEquals(300, document.vote_delay)
-      self.assertEquals(300, document.dist_delay)
-      self.assertEquals([], document.client_versions)
-      self.assertEquals([], document.server_versions)
-      self.assertEquals(expected_flags, set(document.known_flags))
-      self.assertEquals({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
-
-      self.assertEquals(None, document.consensus_method)
-      self.assertEquals({}, document.bandwidth_weights)
-      self.assertEquals(range(1, 13), document.consensus_methods)
-      self.assertEquals(datetime.datetime(2012, 7, 11, 23, 50, 1), document.published)
-      self.assertEquals([], document.get_unrecognized_lines())
+      self.assertEqual(3, document.version)
+      self.assertEqual(None, document.version_flavor)
+      self.assertEqual(False, document.is_consensus)
+      self.assertEqual(True, document.is_vote)
+      self.assertEqual(False, document.is_microdescriptor)
+      self.assertEqual(datetime.datetime(2012, 7, 12, 0, 0, 0), document.valid_after)
+      self.assertEqual(datetime.datetime(2012, 7, 12, 1, 0, 0), document.fresh_until)
+      self.assertEqual(datetime.datetime(2012, 7, 12, 3, 0, 0), document.valid_until)
+      self.assertEqual(300, document.vote_delay)
+      self.assertEqual(300, document.dist_delay)
+      self.assertEqual([], document.client_versions)
+      self.assertEqual([], document.server_versions)
+      self.assertEqual(expected_flags, set(document.known_flags))
+      self.assertEqual({'CircuitPriorityHalflifeMsec': 30000, 'bwauthpid': 1}, document.params)
+
+      self.assertEqual(None, document.consensus_method)
+      self.assertEqual({}, document.bandwidth_weights)
+      self.assertEqual(list(range(1, 13)), document.consensus_methods)
+      self.assertEqual(datetime.datetime(2012, 7, 11, 23, 50, 1), document.published)
+      self.assertEqual([], document.get_unrecognized_lines())
 
       router = document.routers['0013D22389CD50D0B784A3E4061CB31E8CE8CEB5']
-      self.assertEquals('sumkledi', router.nickname)
-      self.assertEquals('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
-      self.assertEquals('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
-      self.assertEquals(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
-      self.assertEquals('178.218.213.229', router.address)
-      self.assertEquals(80, router.or_port)
-      self.assertEquals(None, router.dir_port)
+      self.assertEqual('sumkledi', router.nickname)
+      self.assertEqual('0013D22389CD50D0B784A3E4061CB31E8CE8CEB5', router.fingerprint)
+      self.assertEqual('0799F806200B005F01E40A9A7F1A21C988AE8FB1', router.digest)
+      self.assertEqual(datetime.datetime(2012, 7, 11, 4, 22, 53), router.published)
+      self.assertEqual('178.218.213.229', router.address)
+      self.assertEqual(80, router.or_port)
+      self.assertEqual(None, router.dir_port)
 
       authority = document.directory_authorities[0]
-      self.assertEquals(1, len(document.directory_authorities))
-      self.assertEquals('turtles', authority.nickname)
-      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', authority.fingerprint)
-      self.assertEquals('76.73.17.194', authority.hostname)
-      self.assertEquals('76.73.17.194', authority.address)
-      self.assertEquals(9030, authority.dir_port)
-      self.assertEquals(9090, authority.or_port)
-      self.assertEquals('Mike Perry <email>', authority.contact)
-      self.assertEquals(None, authority.vote_digest)
-      self.assertEquals(None, authority.legacy_dir_key)
+      self.assertEqual(1, len(document.directory_authorities))
+      self.assertEqual('turtles', authority.nickname)
+      self.assertEqual('27B6B5996C426270A5C95488AA5BCEB6BCC86956', authority.fingerprint)
+      self.assertEqual('76.73.17.194', authority.hostname)
+      self.assertEqual('76.73.17.194', authority.address)
+      self.assertEqual(9030, authority.dir_port)
+      self.assertEqual(9090, authority.or_port)
+      self.assertEqual('Mike Perry <email>', authority.contact)
+      self.assertEqual(None, authority.vote_digest)
+      self.assertEqual(None, authority.legacy_dir_key)
 
       certificate = authority.key_certificate
-      self.assertEquals(3, certificate.version)
-      self.assertEquals(None, certificate.address)
-      self.assertEquals(None, certificate.dir_port)
-      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', certificate.fingerprint)
-      self.assertEquals(expected_identity_key, certificate.identity_key)
-      self.assertEquals(datetime.datetime(2011, 11, 28, 21, 51, 4), certificate.published)
-      self.assertEquals(datetime.datetime(2012, 11, 28, 21, 51, 4), certificate.expires)
-      self.assertEquals(expected_signing_key, certificate.signing_key)
-      self.assertEquals(expected_key_crosscert, certificate.crosscert)
-      self.assertEquals(expected_key_certification, certificate.certification)
+      self.assertEqual(3, certificate.version)
+      self.assertEqual(None, certificate.address)
+      self.assertEqual(None, certificate.dir_port)
+      self.assertEqual('27B6B5996C426270A5C95488AA5BCEB6BCC86956', certificate.fingerprint)
+      self.assertEqual(expected_identity_key, certificate.identity_key)
+      self.assertEqual(datetime.datetime(2011, 11, 28, 21, 51, 4), certificate.published)
+      self.assertEqual(datetime.datetime(2012, 11, 28, 21, 51, 4), certificate.expires)
+      self.assertEqual(expected_signing_key, certificate.signing_key)
+      self.assertEqual(expected_key_crosscert, certificate.crosscert)
+      self.assertEqual(expected_key_certification, certificate.certification)
 
       signature = document.signatures[0]
-      self.assertEquals(1, len(document.signatures))
-      self.assertEquals('sha1', signature.method)
-      self.assertEquals('27B6B5996C426270A5C95488AA5BCEB6BCC86956', signature.identity)
-      self.assertEquals('D5C30C15BB3F1DA27669C2D88439939E8F418FCF', signature.key_digest)
-      self.assertEquals(expected_signature, signature.signature)
+      self.assertEqual(1, len(document.signatures))
+      self.assertEqual('sha1', signature.method)
+      self.assertEqual('27B6B5996C426270A5C95488AA5BCEB6BCC86956', signature.identity)
+      self.assertEqual('D5C30C15BB3F1DA27669C2D88439939E8F418FCF', signature.key_digest)
+      self.assertEqual(expected_signature, signature.signature)
 
   def test_minimal_consensus(self):
     """
@@ -382,7 +382,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     consensus = NetworkStatusDocumentV3(consensus_file.read())
     consensus_file.close()
 
-    for router in consensus.routers.values():
+    for router in list(consensus.routers.values()):
       self.assertEqual('caerSidi', router.nickname)
 
     # second example: using stem.descriptor.parse_file
@@ -435,9 +435,9 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     descriptor_file = io.BytesIO(content)
     entries = list(_parse_file(descriptor_file))
 
-    self.assertEquals(entry1, entries[0])
-    self.assertEquals(entry2, entries[1])
-    self.assertEquals(expected_document, entries[0].document)
+    self.assertEqual(entry1, entries[0])
+    self.assertEqual(entry2, entries[1])
+    self.assertEqual(expected_document, entries[0].document)
 
   def test_missing_fields(self):
     """
@@ -461,7 +461,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     """
 
     document = get_network_status_document_v3({'pepperjack': 'is oh so tasty!'})
-    self.assertEquals(['pepperjack is oh so tasty!'], document.get_unrecognized_lines())
+    self.assertEqual(['pepperjack is oh so tasty!'], document.get_unrecognized_lines())
 
   def test_misordered_fields(self):
     """
@@ -472,7 +472,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       attr = {'vote-status': 'consensus'} if is_consensus else {'vote-status': 'vote'}
       lines = get_network_status_document_v3(attr, content = True).split(b'\n')
 
-      for index in xrange(len(lines) - 1):
+      for index in range(len(lines) - 1):
         # once we reach the authority entry or later we're done since swapping
         # those won't be detected
 
@@ -526,22 +526,22 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     """
 
     document = get_network_status_document_v3({'network-status-version': '3'})
-    self.assertEquals(3, document.version)
-    self.assertEquals(None, document.version_flavor)
-    self.assertEquals(False, document.is_microdescriptor)
+    self.assertEqual(3, document.version)
+    self.assertEqual(None, document.version_flavor)
+    self.assertEqual(False, document.is_microdescriptor)
 
     document = get_network_status_document_v3({'network-status-version': '3 microdesc'})
-    self.assertEquals(3, document.version)
-    self.assertEquals('microdesc', document.version_flavor)
-    self.assertEquals(True, document.is_microdescriptor)
+    self.assertEqual(3, document.version)
+    self.assertEqual('microdesc', document.version_flavor)
+    self.assertEqual(True, document.is_microdescriptor)
 
     content = get_network_status_document_v3({'network-status-version': '4'}, content = True)
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEquals(4, document.version)
-    self.assertEquals(None, document.version_flavor)
-    self.assertEquals(False, document.is_microdescriptor)
+    self.assertEqual(4, document.version)
+    self.assertEqual(None, document.version_flavor)
+    self.assertEqual(False, document.is_microdescriptor)
 
   def test_vote_status(self):
     """
@@ -549,13 +549,13 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     """
 
     document = get_network_status_document_v3({'vote-status': 'vote'})
-    self.assertEquals(False, document.is_consensus)
-    self.assertEquals(True, document.is_vote)
+    self.assertEqual(False, document.is_consensus)
+    self.assertEqual(True, document.is_vote)
 
     content = get_network_status_document_v3({'vote-status': 'consensus'}, content = True)
     document = NetworkStatusDocumentV3(content)
-    self.assertEquals(True, document.is_consensus)
-    self.assertEquals(False, document.is_vote)
+    self.assertEqual(True, document.is_consensus)
+    self.assertEqual(False, document.is_vote)
 
     test_values = (
       '',
@@ -568,8 +568,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
       document = NetworkStatusDocumentV3(content, False)
-      self.assertEquals(True, document.is_consensus)
-      self.assertEquals(False, document.is_vote)
+      self.assertEqual(True, document.is_consensus)
+      self.assertEqual(False, document.is_vote)
 
   def test_consensus_methods(self):
     """
@@ -577,13 +577,13 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     """
 
     document = get_network_status_document_v3({'vote-status': 'vote', 'consensus-methods': '12 3 1 780'})
-    self.assertEquals([12, 3, 1, 780], document.consensus_methods)
+    self.assertEqual([12, 3, 1, 780], document.consensus_methods)
 
     # check that we default to including consensus-method 1
     content = get_network_status_document_v3({'vote-status': 'vote'}, ('consensus-methods',), content = True)
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEquals([1], document.consensus_methods)
-    self.assertEquals(None, document.consensus_method)
+    self.assertEqual([1], document.consensus_methods)
+    self.assertEqual(None, document.consensus_method)
 
     test_values = (
       ('', []),
@@ -598,7 +598,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
       document = NetworkStatusDocumentV3(content, False)
-      self.assertEquals(expected_consensus_methods, document.consensus_methods)
+      self.assertEqual(expected_consensus_methods, document.consensus_methods)
 
   def test_consensus_method(self):
     """
@@ -606,13 +606,13 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     """
 
     document = get_network_status_document_v3({'consensus-method': '12'})
-    self.assertEquals(12, document.consensus_method)
+    self.assertEqual(12, document.consensus_method)
 
     # check that we default to being consensus-method 1
     content = get_network_status_document_v3(exclude = ('consensus-method',), content = True)
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEquals(1, document.consensus_method)
-    self.assertEquals([], document.consensus_methods)
+    self.assertEqual(1, document.consensus_method)
+    self.assertEqual([], document.consensus_methods)
 
     test_values = (
       '',
@@ -627,7 +627,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
       document = NetworkStatusDocumentV3(content, False)
-      self.assertEquals(1, document.consensus_method)
+      self.assertEqual(1, document.consensus_method)
 
   def test_time_fields(self):
     """
@@ -646,10 +646,10 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       'valid-until': test_value,
     })
 
-    self.assertEquals(expected, document.published)
-    self.assertEquals(expected, document.valid_after)
-    self.assertEquals(expected, document.fresh_until)
-    self.assertEquals(expected, document.valid_until)
+    self.assertEqual(expected, document.published)
+    self.assertEqual(expected, document.valid_after)
+    self.assertEqual(expected, document.fresh_until)
+    self.assertEqual(expected, document.valid_until)
 
     test_values = (
       '',
@@ -667,7 +667,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
         self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
         document = NetworkStatusDocumentV3(content, False)
-        self.assertEquals(None, getattr(document, attr))
+        self.assertEqual(None, getattr(document, attr))
 
   def test_voting_delay(self):
     """
@@ -675,8 +675,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     """
 
     document = get_network_status_document_v3({'voting-delay': '12 345'})
-    self.assertEquals(12, document.vote_delay)
-    self.assertEquals(345, document.dist_delay)
+    self.assertEqual(12, document.vote_delay)
+    self.assertEqual(345, document.dist_delay)
 
     test_values = (
       '',
@@ -691,8 +691,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
       document = NetworkStatusDocumentV3(content, False)
-      self.assertEquals(None, document.vote_delay)
-      self.assertEquals(None, document.dist_delay)
+      self.assertEqual(None, document.vote_delay)
+      self.assertEqual(None, document.dist_delay)
 
   def test_version_lists(self):
     """
@@ -704,8 +704,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     test_value = '1.2.3.4,56.789.12.34-alpha'
 
     document = get_network_status_document_v3({'client-versions': test_value, 'server-versions': test_value})
-    self.assertEquals(expected, document.client_versions)
-    self.assertEquals(expected, document.server_versions)
+    self.assertEqual(expected, document.client_versions)
+    self.assertEqual(expected, document.server_versions)
 
     test_values = (
       ('', []),
@@ -722,7 +722,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
         self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
         document = NetworkStatusDocumentV3(content, False)
-        self.assertEquals(expected_value, getattr(document, attr))
+        self.assertEqual(expected_value, getattr(document, attr))
 
   def test_known_flags(self):
     """
@@ -742,7 +742,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
     for test_value, expected_value in test_values:
       document = get_network_status_document_v3({'known-flags': test_value})
-      self.assertEquals(expected_value, document.known_flags)
+      self.assertEqual(expected_value, document.known_flags)
 
   def test_flag_thresholds(self):
     """
@@ -758,7 +758,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
     for test_value, expected_value in test_values:
       document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': test_value})
-      self.assertEquals(expected_value, document.flag_thresholds)
+      self.assertEqual(expected_value, document.flag_thresholds)
 
     # parses a full entry found in an actual vote
 
@@ -776,7 +776,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     }
 
     document = get_network_status_document_v3({'vote-status': 'vote', 'flag-thresholds': full_line})
-    self.assertEquals(expected_value, document.flag_thresholds)
+    self.assertEqual(expected_value, document.flag_thresholds)
 
     test_values = (
       'stable-uptime 693369',   # not a key=value mapping
@@ -790,7 +790,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
       document = NetworkStatusDocumentV3(content, False)
-      self.assertEquals({}, document.flag_thresholds)
+      self.assertEqual({}, document.flag_thresholds)
 
   def test_params(self):
     """
@@ -798,18 +798,18 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     """
 
     document = get_network_status_document_v3({'params': 'CircuitPriorityHalflifeMsec=30000 bwauthpid=1 unrecognized=-122'})
-    self.assertEquals(30000, document.params['CircuitPriorityHalflifeMsec'])
-    self.assertEquals(1, document.params['bwauthpid'])
-    self.assertEquals(-122, document.params['unrecognized'])
+    self.assertEqual(30000, document.params['CircuitPriorityHalflifeMsec'])
+    self.assertEqual(1, document.params['bwauthpid'])
+    self.assertEqual(-122, document.params['unrecognized'])
 
     # empty params line
     content = get_network_status_document_v3({'params': ''}, content = True)
     document = NetworkStatusDocumentV3(content, default_params = True)
-    self.assertEquals(DEFAULT_PARAMS, document.params)
+    self.assertEqual(DEFAULT_PARAMS, document.params)
 
     content = get_network_status_document_v3({'params': ''}, content = True)
     document = NetworkStatusDocumentV3(content, default_params = False)
-    self.assertEquals({}, document.params)
+    self.assertEqual({}, document.params)
 
   def test_params_malformed(self):
     """
@@ -828,7 +828,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
       document = NetworkStatusDocumentV3(content, False)
-      self.assertEquals(DEFAULT_PARAMS, document.params)
+      self.assertEqual(DEFAULT_PARAMS, document.params)
 
   def test_params_range(self):
     """
@@ -861,7 +861,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
         self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
         document = NetworkStatusDocumentV3(content, False, default_params = False)
 
-      self.assertEquals(expected_value, document.params)
+      self.assertEqual(expected_value, document.params)
 
   def test_params_misordered(self):
     """
@@ -872,7 +872,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, False, default_params = False)
-    self.assertEquals({'unrecognized': -122, 'bwauthpid': 1}, document.params)
+    self.assertEqual({'unrecognized': -122, 'bwauthpid': 1}, document.params)
 
   def test_footer_consensus_method_requirement(self):
     """
@@ -936,7 +936,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
       expected[key] = index - 5
 
     document = get_network_status_document_v3({'bandwidth-weights': ' '.join(weight_entries)})
-    self.assertEquals(expected, document.bandwidth_weights)
+    self.assertEqual(expected, document.bandwidth_weights)
 
   def test_bandwidth_wights_malformed(self):
     """
@@ -959,7 +959,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
       self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
       document = NetworkStatusDocumentV3(content, False)
-      self.assertEquals(expected, document.bandwidth_weights)
+      self.assertEqual(expected, document.bandwidth_weights)
 
   def test_bandwidth_wights_misordered(self):
     """
@@ -973,7 +973,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEquals(expected, document.bandwidth_weights)
+    self.assertEqual(expected, document.bandwidth_weights)
 
   def test_bandwidth_wights_in_vote(self):
     """
@@ -987,7 +987,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEquals(expected, document.bandwidth_weights)
+    self.assertEqual(expected, document.bandwidth_weights)
 
   def test_microdescriptor_signature(self):
     """
@@ -1024,7 +1024,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     )
 
     for test_value in test_values:
-      for test_attr in xrange(3):
+      for test_attr in range(3):
         attrs = [DOC_SIG.identity, DOC_SIG.key_digest, DOC_SIG.signature]
         attrs[test_attr] = test_value
 
@@ -1047,8 +1047,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
     document = get_network_status_document_v3(routers = (entry1, entry2))
 
-    self.assertTrue(entry1 in document.routers.values())
-    self.assertTrue(entry2 in document.routers.values())
+    self.assertTrue(entry1 in list(document.routers.values()))
+    self.assertTrue(entry2 in list(document.routers.values()))
 
     # try with an invalid RouterStatusEntry
 
@@ -1057,7 +1057,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEquals([entry3], document.routers.values())
+    self.assertEqual([entry3], list(document.routers.values()))
 
     # try including with a microdescriptor consensus
 
@@ -1065,7 +1065,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEqual([RouterStatusEntryMicroV3(str(entry1), False)], document.routers.values())
+    self.assertEqual([RouterStatusEntryMicroV3(str(entry1), False)], list(document.routers.values()))
 
   def test_with_microdescriptor_router_status_entries(self):
     """
@@ -1081,8 +1081,8 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
     document = get_network_status_document_v3({'network-status-version': '3 microdesc'}, routers = (entry1, entry2))
 
-    self.assertTrue(entry1 in document.routers.values())
-    self.assertTrue(entry2 in document.routers.values())
+    self.assertTrue(entry1 in list(document.routers.values()))
+    self.assertTrue(entry2 in list(document.routers.values()))
 
     # try with an invalid RouterStatusEntry
 
@@ -1092,7 +1092,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEquals([entry3], document.routers.values())
+    self.assertEqual([entry3], list(document.routers.values()))
 
     # try including microdescriptor entry in a normal consensus
 
@@ -1100,7 +1100,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, False)
-    self.assertEqual([RouterStatusEntryV3(str(entry1), False)], document.routers.values())
+    self.assertEqual([RouterStatusEntryV3(str(entry1), False)], list(document.routers.values()))
 
   def test_with_directory_authorities(self):
     """
@@ -1124,12 +1124,12 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
           else:
             document = NetworkStatusDocumentV3(content)
 
-          self.assertEquals((authority1, authority2), document.directory_authorities)
+          self.assertEqual((authority1, authority2), document.directory_authorities)
         else:
           # authority votes in a consensus or consensus authorities in a vote
           self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
           document = NetworkStatusDocumentV3(content, validate = False)
-          self.assertEquals((authority1, authority2), document.directory_authorities)
+          self.assertEqual((authority1, authority2), document.directory_authorities)
 
   def test_with_legacy_directory_authorities(self):
     """
@@ -1144,7 +1144,7 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
 
     document = get_network_status_document_v3({'vote-status': 'consensus'}, authorities = (authority1, authority2, authority3))
 
-    self.assertEquals((authority1, authority2, authority3), document.directory_authorities)
+    self.assertEqual((authority1, authority2, authority3), document.directory_authorities)
 
   def test_authority_validation_flag_propagation(self):
     """
@@ -1162,4 +1162,4 @@ DnN5aFtYKiTc19qIC7Nmo+afPdDEf0MlJvEOP5EWl3w=
     self.assertRaises(ValueError, NetworkStatusDocumentV3, content)
 
     document = NetworkStatusDocumentV3(content, validate = False)
-    self.assertEquals((authority,), document.directory_authorities)
+    self.assertEqual((authority,), document.directory_authorities)
diff --git a/test/unit/descriptor/networkstatus/key_certificate.py b/test/unit/descriptor/networkstatus/key_certificate.py
index 8d0e828..8939c7e 100644
--- a/test/unit/descriptor/networkstatus/key_certificate.py
+++ b/test/unit/descriptor/networkstatus/key_certificate.py
@@ -41,7 +41,7 @@ class TestKeyCertificate(unittest.TestCase):
     """
 
     certificate = get_key_certificate({'pepperjack': 'is oh so tasty!'})
-    self.assertEquals(['pepperjack is oh so tasty!'], certificate.get_unrecognized_lines())
+    self.assertEqual(['pepperjack is oh so tasty!'], certificate.get_unrecognized_lines())
 
   def test_first_and_last_lines(self):
     """
@@ -92,15 +92,15 @@ class TestKeyCertificate(unittest.TestCase):
     """
 
     certificate = get_key_certificate({'dir-key-certificate-version': '3'})
-    self.assertEquals(3, certificate.version)
+    self.assertEqual(3, certificate.version)
 
     content = get_key_certificate({'dir-key-certificate-version': '4'}, content = True)
     self.assertRaises(ValueError, KeyCertificate, content)
-    self.assertEquals(4, KeyCertificate(content, False).version)
+    self.assertEqual(4, KeyCertificate(content, False).version)
 
     content = get_key_certificate({'dir-key-certificate-version': 'boo'}, content = True)
     self.assertRaises(ValueError, KeyCertificate, content)
-    self.assertEquals(None, KeyCertificate(content, False).version)
+    self.assertEqual(None, KeyCertificate(content, False).version)
 
   def test_dir_address(self):
     """
@@ -169,7 +169,7 @@ class TestKeyCertificate(unittest.TestCase):
         self.assertRaises(ValueError, KeyCertificate, content)
 
         certificate = KeyCertificate(content, False)
-        self.assertEquals(None, getattr(certificate, attr))
+        self.assertEqual(None, getattr(certificate, attr))
 
   def test_key_blocks(self):
     """
@@ -192,7 +192,7 @@ class TestKeyCertificate(unittest.TestCase):
       self.assertRaises(ValueError, KeyCertificate, content)
 
       certificate = KeyCertificate(content, False)
-      self.assertEquals(None, getattr(certificate, attr))
+      self.assertEqual(None, getattr(certificate, attr))
 
   def test_wrong_block_type(self):
     """
diff --git a/test/unit/descriptor/reader.py b/test/unit/descriptor/reader.py
index f19c0ec..31c694e 100644
--- a/test/unit/descriptor/reader.py
+++ b/test/unit/descriptor/reader.py
@@ -3,7 +3,6 @@ Unit tests for stem.descriptor.reader.
 """
 
 import getpass
-import io
 import os
 import shutil
 import signal
@@ -12,6 +11,7 @@ import tarfile
 import tempfile
 import time
 import unittest
+from io import StringIO
 
 import stem.descriptor.reader
 import test.runner
@@ -107,8 +107,8 @@ class TestDescriptorReader(unittest.TestCase):
       '/dir/after empty line': 12345,
     }
 
-    open_mock.return_value = io.StringIO(u'\n'.join(test_lines))
-    self.assertEquals(expected_value, stem.descriptor.reader.load_processed_files(''))
+    open_mock.return_value = StringIO(u'\n'.join(test_lines))
+    self.assertEqual(expected_value, stem.descriptor.reader.load_processed_files(''))
 
   @patch('stem.descriptor.reader.open', create = True)
   def test_load_processed_files_empty(self, open_mock):
@@ -116,8 +116,8 @@ class TestDescriptorReader(unittest.TestCase):
     Tests the load_processed_files() function with an empty file.
     """
 
-    open_mock.return_value = io.StringIO(u'')
-    self.assertEquals({}, stem.descriptor.reader.load_processed_files(''))
+    open_mock.return_value = StringIO(u'')
+    self.assertEqual({}, stem.descriptor.reader.load_processed_files(''))
 
   @patch('stem.descriptor.reader.open', create = True)
   def test_load_processed_files_no_file(self, open_mock):
@@ -126,7 +126,7 @@ class TestDescriptorReader(unittest.TestCase):
     it is missing the file path.
     """
 
-    open_mock.return_value = io.StringIO(u' 12345')
+    open_mock.return_value = StringIO(u' 12345')
     self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
 
   @patch('stem.descriptor.reader.open', create = True)
@@ -136,7 +136,7 @@ class TestDescriptorReader(unittest.TestCase):
     it is missing the timestamp.
     """
 
-    open_mock.return_value = io.StringIO(u'/dir/file ')
+    open_mock.return_value = StringIO(u'/dir/file ')
     self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
 
   @patch('stem.descriptor.reader.open', create = True)
@@ -146,7 +146,7 @@ class TestDescriptorReader(unittest.TestCase):
     it has an invalid file path.
     """
 
-    open_mock.return_value = io.StringIO(u'not_an_absolute_file 12345')
+    open_mock.return_value = StringIO(u'not_an_absolute_file 12345')
     self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
 
   @patch('stem.descriptor.reader.open', create = True)
@@ -156,7 +156,7 @@ class TestDescriptorReader(unittest.TestCase):
     it has a non-numeric timestamp.
     """
 
-    open_mock.return_value = io.StringIO(u'/dir/file 123a')
+    open_mock.return_value = StringIO(u'/dir/file 123a')
     self.assertRaises(TypeError, stem.descriptor.reader.load_processed_files, '')
 
   def test_load_processed_files_from_data(self):
@@ -173,7 +173,7 @@ class TestDescriptorReader(unittest.TestCase):
       '/file with spaces/and \\ stuff': 890,
     }
 
-    self.assertEquals(expected_listing, loaded_listing)
+    self.assertEqual(expected_listing, loaded_listing)
 
   def test_load_processed_files_missing(self):
     """
@@ -202,7 +202,7 @@ class TestDescriptorReader(unittest.TestCase):
       test.runner.skip(self, '(chmod not functional)')
 
     test_listing_path = self._make_processed_files_listing(BASIC_LISTING)
-    os.chmod(test_listing_path, 0077)  # remove read permissions
+    os.chmod(test_listing_path, 0o077)  # remove read permissions
     self.assertRaises(IOError, stem.descriptor.reader.load_processed_files, test_listing_path)
 
   def test_save_processed_files(self):
@@ -221,7 +221,7 @@ class TestDescriptorReader(unittest.TestCase):
     stem.descriptor.reader.save_processed_files(self.test_listing_path, initial_listing)
     loaded_listing = stem.descriptor.reader.load_processed_files(self.test_listing_path)
 
-    self.assertEquals(initial_listing, loaded_listing)
+    self.assertEqual(initial_listing, loaded_listing)
 
   def test_save_processed_files_malformed(self):
     """
@@ -262,7 +262,7 @@ class TestDescriptorReader(unittest.TestCase):
 
     # running this test multiple times to flush out concurrency issues
 
-    for _ in xrange(15):
+    for _ in range(15):
       remaining_entries = list(descriptor_entries)
 
       with stem.descriptor.reader.DescriptorReader(descriptor_path) as reader:
@@ -288,20 +288,20 @@ class TestDescriptorReader(unittest.TestCase):
     reader = stem.descriptor.reader.DescriptorReader(descriptor_path)
 
     with reader:
-      self.assertEquals(1, len(list(reader)))
+      self.assertEqual(1, len(list(reader)))
 
     # run it a second time, this shouldn't provide any descriptors because we
     # have already read it
 
     with reader:
-      self.assertEquals(0, len(list(reader)))
+      self.assertEqual(0, len(list(reader)))
 
     # clear the DescriptorReader's memory of seeing the file and run it again
 
     reader.set_processed_files([])
 
     with reader:
-      self.assertEquals(1, len(list(reader)))
+      self.assertEqual(1, len(list(reader)))
 
   def test_buffer_size(self):
     """
@@ -377,7 +377,7 @@ class TestDescriptorReader(unittest.TestCase):
 
     with stem.descriptor.reader.DescriptorReader(test_path) as reader:
       read_descriptors = [str(desc) for desc in list(reader)]
-      self.assertEquals(expected_results, read_descriptors)
+      self.assertEqual(expected_results, read_descriptors)
 
   def test_archived_gzip(self):
     """
@@ -389,7 +389,7 @@ class TestDescriptorReader(unittest.TestCase):
 
     with stem.descriptor.reader.DescriptorReader(test_path) as reader:
       read_descriptors = [str(desc) for desc in list(reader)]
-      self.assertEquals(expected_results, read_descriptors)
+      self.assertEqual(expected_results, read_descriptors)
 
   def test_archived_bz2(self):
     """
@@ -401,7 +401,7 @@ class TestDescriptorReader(unittest.TestCase):
 
     with stem.descriptor.reader.DescriptorReader(test_path) as reader:
       read_descriptors = [str(desc) for desc in list(reader)]
-      self.assertEquals(expected_results, read_descriptors)
+      self.assertEqual(expected_results, read_descriptors)
 
   def test_stop(self):
     """
@@ -452,7 +452,7 @@ class TestDescriptorReader(unittest.TestCase):
     with reader:
       list(reader)  # iterates over all of the descriptors
 
-    self.assertEquals(expected_results, reader.get_processed_files())
+    self.assertEqual(expected_results, reader.get_processed_files())
 
   def test_skip_nondescriptor_contents(self):
     """
@@ -495,24 +495,24 @@ class TestDescriptorReader(unittest.TestCase):
     # path that we want the DescriptorReader to skip
 
     test_path = os.path.join(DESCRIPTOR_TEST_DATA, 'example_descriptor')
-    initial_processed_files = {test_path: sys.maxint}
+    initial_processed_files = {test_path: sys.maxsize}
 
     skip_listener = SkipListener()
     reader = stem.descriptor.reader.DescriptorReader(test_path)
     reader.register_skip_listener(skip_listener.listener)
     reader.set_processed_files(initial_processed_files)
 
-    self.assertEquals(initial_processed_files, reader.get_processed_files())
+    self.assertEqual(initial_processed_files, reader.get_processed_files())
 
     with reader:
       list(reader)  # iterates over all of the descriptors
 
-    self.assertEquals(1, len(skip_listener.results))
+    self.assertEqual(1, len(skip_listener.results))
 
     skipped_path, skip_exception = skip_listener.results[0]
     self.assertEqual(test_path, skipped_path)
     self.assertTrue(isinstance(skip_exception, stem.descriptor.reader.AlreadyRead))
-    self.assertEqual(sys.maxint, skip_exception.last_modified_when_read)
+    self.assertEqual(sys.maxsize, skip_exception.last_modified_when_read)
 
   def test_skip_listener_unrecognized_type(self):
     """
@@ -567,7 +567,7 @@ class TestDescriptorReader(unittest.TestCase):
       test_file.write('test data for test_skip_listener_unrecognized_type()')
       test_file.close()
 
-      os.chmod(test_path, 0077)  # remove read permissions
+      os.chmod(test_path, 0o077)  # remove read permissions
 
       skip_listener = SkipListener()
       reader = stem.descriptor.reader.DescriptorReader(test_path)
diff --git a/test/unit/descriptor/router_status_entry.py b/test/unit/descriptor/router_status_entry.py
index 4c9a13a..603cb86 100644
--- a/test/unit/descriptor/router_status_entry.py
+++ b/test/unit/descriptor/router_status_entry.py
@@ -34,7 +34,7 @@ class TestRouterStatusEntry(unittest.TestCase):
       '/nHdqoKZ6bKZixxAPzYt9Qen+Is': 'FE71DDAA8299E9B2998B1C403F362DF507A7F88B',
     }
 
-    for arg, expected in test_values.items():
+    for arg, expected in list(test_values.items()):
       self.assertEqual(expected, _base64_to_hex(arg, True))
 
     # checks with some malformed inputs
@@ -130,7 +130,7 @@ class TestRouterStatusEntry(unittest.TestCase):
     """
 
     entry = get_router_status_entry_v3({'z': 'New tor feature: sparkly unicorns!'})
-    self.assertEquals(['z New tor feature: sparkly unicorns!'], entry.get_unrecognized_lines())
+    self.assertEqual(['z New tor feature: sparkly unicorns!'], entry.get_unrecognized_lines())
 
   def test_proceeding_line(self):
     """
@@ -319,9 +319,9 @@ class TestRouterStatusEntry(unittest.TestCase):
         ('2607:fcd0:daaa:101::602c:bd62', 443, True)],
     }
 
-    for a_line, expected in test_values.items():
+    for a_line, expected in list(test_values.items()):
       entry = get_router_status_entry_v3({'a': a_line})
-      self.assertEquals(expected, entry.or_addresses)
+      self.assertEqual(expected, entry.or_addresses)
 
     # includes multiple 'a' lines
 
@@ -335,7 +335,7 @@ class TestRouterStatusEntry(unittest.TestCase):
     ]
 
     entry = RouterStatusEntryV3(content)
-    self.assertEquals(expected, entry.or_addresses)
+    self.assertEqual(expected, entry.or_addresses)
 
     # tries some invalid inputs
 
@@ -360,9 +360,9 @@ class TestRouterStatusEntry(unittest.TestCase):
       'Ugabuga': ['Ugabuga'],
     }
 
-    for s_line, expected in test_values.items():
+    for s_line, expected in list(test_values.items()):
       entry = get_router_status_entry_v3({'s': s_line})
-      self.assertEquals(expected, entry.flags)
+      self.assertEqual(expected, entry.flags)
 
     # tries some invalid inputs
     test_values = {
@@ -371,7 +371,7 @@ class TestRouterStatusEntry(unittest.TestCase):
       'Fast Fast': [Flag.FAST, Flag.FAST],
     }
 
-    for s_line, expected in test_values.items():
+    for s_line, expected in list(test_values.items()):
       content = get_router_status_entry_v3({'s': s_line}, content = True)
       self._expect_invalid_attr(content, 'flags', expected)
 
@@ -387,10 +387,10 @@ class TestRouterStatusEntry(unittest.TestCase):
       'new_stuff and stuff': None,
     }
 
-    for v_line, expected in test_values.items():
+    for v_line, expected in list(test_values.items()):
       entry = get_router_status_entry_v3({'v': v_line})
-      self.assertEquals(expected, entry.version)
-      self.assertEquals(v_line, entry.version_line)
+      self.assertEqual(expected, entry.version)
+      self.assertEqual(v_line, entry.version_line)
 
     # tries an invalid input
     content = get_router_status_entry_v3({'v': 'Tor ugabuga'}, content = True)
@@ -409,12 +409,12 @@ class TestRouterStatusEntry(unittest.TestCase):
       'Bandwidth=11111 Measured=482 Unmeasured=1 Blarg!': (11111, 482, True, ['Blarg!']),
     }
 
-    for w_line, expected in test_values.items():
+    for w_line, expected in list(test_values.items()):
       entry = get_router_status_entry_v3({'w': w_line})
-      self.assertEquals(expected[0], entry.bandwidth)
-      self.assertEquals(expected[1], entry.measured)
-      self.assertEquals(expected[2], entry.is_unmeasured)
-      self.assertEquals(expected[3], entry.unrecognized_bandwidth_entries)
+      self.assertEqual(expected[0], entry.bandwidth)
+      self.assertEqual(expected[1], entry.measured)
+      self.assertEqual(expected[2], entry.is_unmeasured)
+      self.assertEqual(expected[3], entry.unrecognized_bandwidth_entries)
 
     # tries some invalid inputs
     test_values = (
@@ -449,9 +449,9 @@ class TestRouterStatusEntry(unittest.TestCase):
       'accept 80,110,143,443': MicroExitPolicy('accept 80,110,143,443'),
     }
 
-    for p_line, expected in test_values.items():
+    for p_line, expected in list(test_values.items()):
       entry = get_router_status_entry_v3({'p': p_line})
-      self.assertEquals(expected, entry.exit_policy)
+      self.assertEqual(expected, entry.exit_policy)
 
     # tries some invalid inputs
     test_values = (
@@ -484,10 +484,10 @@ class TestRouterStatusEntry(unittest.TestCase):
     setattr(mock_document, 'is_vote', True)
     setattr(mock_document, 'is_consensus', False)
 
-    for m_line, expected in test_values.items():
+    for m_line, expected in list(test_values.items()):
       content = get_router_status_entry_v3({'m': m_line}, content = True)
       entry = RouterStatusEntryV3(content, document = mock_document)
-      self.assertEquals(expected, entry.microdescriptor_hashes)
+      self.assertEqual(expected, entry.microdescriptor_hashes)
 
     # try with multiple 'm' lines
 
@@ -501,7 +501,7 @@ class TestRouterStatusEntry(unittest.TestCase):
     ]
 
     entry = RouterStatusEntryV3(content, document = mock_document)
-    self.assertEquals(expected, entry.microdescriptor_hashes)
+    self.assertEqual(expected, entry.microdescriptor_hashes)
 
     # try without a document
     content = get_router_status_entry_v3({'m': '8,9,10,11,12'}, content = True)
@@ -529,6 +529,6 @@ class TestRouterStatusEntry(unittest.TestCase):
     entry = RouterStatusEntryV3(content, False)
 
     if attr:
-      self.assertEquals(expected_value, getattr(entry, attr))
+      self.assertEqual(expected_value, getattr(entry, attr))
     else:
-      self.assertEquals('caerSidi', entry.nickname)
+      self.assertEqual('caerSidi', entry.nickname)
diff --git a/test/unit/descriptor/server_descriptor.py b/test/unit/descriptor/server_descriptor.py
index 97fcde3..c05dee1 100644
--- a/test/unit/descriptor/server_descriptor.py
+++ b/test/unit/descriptor/server_descriptor.py
@@ -101,35 +101,35 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 -----END SIGNATURE-----"""
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-    self.assertEquals('caerSidi', desc.nickname)
-    self.assertEquals('A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB', desc.fingerprint)
-    self.assertEquals('71.35.133.197', desc.address)
-    self.assertEquals(9001, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(None, desc.dir_port)
-    self.assertEquals(b'Tor 0.2.1.30 on Linux x86_64', desc.platform)
-    self.assertEquals(stem.version.Version('0.2.1.30'), desc.tor_version)
-    self.assertEquals('Linux x86_64', desc.operating_system)
-    self.assertEquals(588217, desc.uptime)
-    self.assertEquals(datetime.datetime(2012, 3, 1, 17, 15, 27), desc.published)
-    self.assertEquals(b'www.atagar.com/contact', desc.contact)
-    self.assertEquals(['1', '2'], desc.link_protocols)
-    self.assertEquals(['1'], desc.circuit_protocols)
-    self.assertEquals(False, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals('D225B728768D7EA4B5587C13A7A9D22EBBEE6E66', desc.extra_info_digest)
-    self.assertEquals(['2'], desc.hidden_service_dir)
-    self.assertEquals(expected_family, desc.family)
-    self.assertEquals(153600, desc.average_bandwidth)
-    self.assertEquals(256000, desc.burst_bandwidth)
-    self.assertEquals(104590, desc.observed_bandwidth)
-    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
-    self.assertEquals(expected_onion_key, desc.onion_key)
-    self.assertEquals(expected_signing_key, desc.signing_key)
-    self.assertEquals(expected_signature, desc.signature)
-    self.assertEquals([], desc.get_unrecognized_lines())
-    self.assertEquals('2C7B27BEAB04B4E2459D89CA6D5CD1CC5F95A689', desc.digest())
+    self.assertEqual('caerSidi', desc.nickname)
+    self.assertEqual('A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB', desc.fingerprint)
+    self.assertEqual('71.35.133.197', desc.address)
+    self.assertEqual(9001, desc.or_port)
+    self.assertEqual(None, desc.socks_port)
+    self.assertEqual(None, desc.dir_port)
+    self.assertEqual(b'Tor 0.2.1.30 on Linux x86_64', desc.platform)
+    self.assertEqual(stem.version.Version('0.2.1.30'), desc.tor_version)
+    self.assertEqual('Linux x86_64', desc.operating_system)
+    self.assertEqual(588217, desc.uptime)
+    self.assertEqual(datetime.datetime(2012, 3, 1, 17, 15, 27), desc.published)
+    self.assertEqual(b'www.atagar.com/contact', desc.contact)
+    self.assertEqual(['1', '2'], desc.link_protocols)
+    self.assertEqual(['1'], desc.circuit_protocols)
+    self.assertEqual(False, desc.hibernating)
+    self.assertEqual(False, desc.allow_single_hop_exits)
+    self.assertEqual(False, desc.extra_info_cache)
+    self.assertEqual('D225B728768D7EA4B5587C13A7A9D22EBBEE6E66', desc.extra_info_digest)
+    self.assertEqual(['2'], desc.hidden_service_dir)
+    self.assertEqual(expected_family, desc.family)
+    self.assertEqual(153600, desc.average_bandwidth)
+    self.assertEqual(256000, desc.burst_bandwidth)
+    self.assertEqual(104590, desc.observed_bandwidth)
+    self.assertEqual(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
+    self.assertEqual(expected_onion_key, desc.onion_key)
+    self.assertEqual(expected_signing_key, desc.signing_key)
+    self.assertEqual(expected_signature, desc.signature)
+    self.assertEqual([], desc.get_unrecognized_lines())
+    self.assertEqual('2C7B27BEAB04B4E2459D89CA6D5CD1CC5F95A689', desc.digest())
 
   def test_metrics_descriptor_multiple(self):
     """
@@ -139,13 +139,13 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     with open(get_resource('metrics_server_desc_multiple'), 'rb') as descriptor_file:
       descriptors = list(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
 
-      self.assertEquals(2, len(descriptors))
+      self.assertEqual(2, len(descriptors))
 
-      self.assertEquals('anonion', descriptors[0].nickname)
-      self.assertEquals('9A5EC5BB866517E53962AF4D3E776536694B069E', descriptors[0].fingerprint)
+      self.assertEqual('anonion', descriptors[0].nickname)
+      self.assertEqual('9A5EC5BB866517E53962AF4D3E776536694B069E', descriptors[0].fingerprint)
 
-      self.assertEquals('Unnamed', descriptors[1].nickname)
-      self.assertEquals('5366F1D198759F8894EA6E5FF768C667F59AFD24', descriptors[1].fingerprint)
+      self.assertEqual('Unnamed', descriptors[1].nickname)
+      self.assertEqual('5366F1D198759F8894EA6E5FF768C667F59AFD24', descriptors[1].fingerprint)
 
   def test_old_descriptor(self):
     """
@@ -155,43 +155,43 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     descriptor_file = open(get_resource('old_descriptor'), 'rb')
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-    self.assertEquals('krypton', desc.nickname)
-    self.assertEquals('3E2F63E2356F52318B536A12B6445373808A5D6C', desc.fingerprint)
-    self.assertEquals('212.37.39.59', desc.address)
-    self.assertEquals(8000, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(None, desc.dir_port)
-    self.assertEquals(b'Tor 0.1.0.14 on FreeBSD i386', desc.platform)
-    self.assertEquals(stem.version.Version('0.1.0.14'), desc.tor_version)
-    self.assertEquals('FreeBSD i386', desc.operating_system)
-    self.assertEquals(64820, desc.uptime)
-    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 1, 3), desc.published)
-    self.assertEquals(None, desc.contact)
-    self.assertEquals(None, desc.link_protocols)
-    self.assertEquals(None, desc.circuit_protocols)
-    self.assertEquals(True, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals(None, desc.extra_info_digest)
-    self.assertEquals(None, desc.hidden_service_dir)
-    self.assertEquals(set(), desc.family)
-    self.assertEquals(102400, desc.average_bandwidth)
-    self.assertEquals(10485760, desc.burst_bandwidth)
-    self.assertEquals(0, desc.observed_bandwidth)
-    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.read_history_end)
-    self.assertEquals(900, desc.read_history_interval)
-    self.assertEquals(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.write_history_end)
-    self.assertEquals(900, desc.write_history_interval)
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual('krypton', desc.nickname)
+    self.assertEqual('3E2F63E2356F52318B536A12B6445373808A5D6C', desc.fingerprint)
+    self.assertEqual('212.37.39.59', desc.address)
+    self.assertEqual(8000, desc.or_port)
+    self.assertEqual(None, desc.socks_port)
+    self.assertEqual(None, desc.dir_port)
+    self.assertEqual(b'Tor 0.1.0.14 on FreeBSD i386', desc.platform)
+    self.assertEqual(stem.version.Version('0.1.0.14'), desc.tor_version)
+    self.assertEqual('FreeBSD i386', desc.operating_system)
+    self.assertEqual(64820, desc.uptime)
+    self.assertEqual(datetime.datetime(2005, 12, 16, 18, 1, 3), desc.published)
+    self.assertEqual(None, desc.contact)
+    self.assertEqual(None, desc.link_protocols)
+    self.assertEqual(None, desc.circuit_protocols)
+    self.assertEqual(True, desc.hibernating)
+    self.assertEqual(False, desc.allow_single_hop_exits)
+    self.assertEqual(False, desc.extra_info_cache)
+    self.assertEqual(None, desc.extra_info_digest)
+    self.assertEqual(None, desc.hidden_service_dir)
+    self.assertEqual(set(), desc.family)
+    self.assertEqual(102400, desc.average_bandwidth)
+    self.assertEqual(10485760, desc.burst_bandwidth)
+    self.assertEqual(0, desc.observed_bandwidth)
+    self.assertEqual(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.read_history_end)
+    self.assertEqual(900, desc.read_history_interval)
+    self.assertEqual(datetime.datetime(2005, 12, 16, 18, 0, 48), desc.write_history_end)
+    self.assertEqual(900, desc.write_history_interval)
+    self.assertEqual([], desc.get_unrecognized_lines())
 
     # The read-history and write-history lines are pretty long so just checking
     # the initial contents for the line and parsed values.
 
     read_values_start = [20774, 489973, 510022, 511163, 20949]
-    self.assertEquals(read_values_start, desc.read_history_values[:5])
+    self.assertEqual(read_values_start, desc.read_history_values[:5])
 
     write_values_start = [81, 8848, 8927, 8927, 83, 8848, 8931, 8929, 81, 8846]
-    self.assertEquals(write_values_start, desc.write_history_values[:10])
+    self.assertEqual(write_values_start, desc.write_history_values[:10])
 
   def test_non_ascii_descriptor(self):
     """
@@ -203,31 +203,31 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     expected_contact = b'1024D/04D2E818 L\xc3\xa9na\xc3\xafc Huard <lenaic dot huard AT laposte dot net>'
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
-    self.assertEquals('Coruscant', desc.nickname)
-    self.assertEquals('0B9821545C48E496AEED9ECC0DB506C49FF8158D', desc.fingerprint)
-    self.assertEquals('88.182.161.122', desc.address)
-    self.assertEquals(9001, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(9030, desc.dir_port)
-    self.assertEquals(b'Tor 0.2.3.25 on Linux', desc.platform)
-    self.assertEquals(stem.version.Version('0.2.3.25'), desc.tor_version)
-    self.assertEquals('Linux', desc.operating_system)
-    self.assertEquals(259738, desc.uptime)
-    self.assertEquals(datetime.datetime(2013, 5, 18, 11, 16, 19), desc.published)
-    self.assertEquals(expected_contact, desc.contact)
-    self.assertEquals(['1', '2'], desc.link_protocols)
-    self.assertEquals(['1'], desc.circuit_protocols)
-    self.assertEquals(False, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals('56403D838DE152421CD401B8E57DAD4483A3D56B', desc.extra_info_digest)
-    self.assertEquals(['2'], desc.hidden_service_dir)
-    self.assertEquals(set(), desc.family)
-    self.assertEquals(102400, desc.average_bandwidth)
-    self.assertEquals(204800, desc.burst_bandwidth)
-    self.assertEquals(122818, desc.observed_bandwidth)
-    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual('Coruscant', desc.nickname)
+    self.assertEqual('0B9821545C48E496AEED9ECC0DB506C49FF8158D', desc.fingerprint)
+    self.assertEqual('88.182.161.122', desc.address)
+    self.assertEqual(9001, desc.or_port)
+    self.assertEqual(None, desc.socks_port)
+    self.assertEqual(9030, desc.dir_port)
+    self.assertEqual(b'Tor 0.2.3.25 on Linux', desc.platform)
+    self.assertEqual(stem.version.Version('0.2.3.25'), desc.tor_version)
+    self.assertEqual('Linux', desc.operating_system)
+    self.assertEqual(259738, desc.uptime)
+    self.assertEqual(datetime.datetime(2013, 5, 18, 11, 16, 19), desc.published)
+    self.assertEqual(expected_contact, desc.contact)
+    self.assertEqual(['1', '2'], desc.link_protocols)
+    self.assertEqual(['1'], desc.circuit_protocols)
+    self.assertEqual(False, desc.hibernating)
+    self.assertEqual(False, desc.allow_single_hop_exits)
+    self.assertEqual(False, desc.extra_info_cache)
+    self.assertEqual('56403D838DE152421CD401B8E57DAD4483A3D56B', desc.extra_info_digest)
+    self.assertEqual(['2'], desc.hidden_service_dir)
+    self.assertEqual(set(), desc.family)
+    self.assertEqual(102400, desc.average_bandwidth)
+    self.assertEqual(204800, desc.burst_bandwidth)
+    self.assertEqual(122818, desc.observed_bandwidth)
+    self.assertEqual(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
+    self.assertEqual([], desc.get_unrecognized_lines())
 
     # Make sure that we can get a string representation for this descriptor
     # (having non-unicode content risks a UnicodeEncodeError)...
@@ -245,9 +245,9 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     descriptor_file = open(get_resource('cr_in_contact_line'), 'rb')
     desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
 
-    self.assertEquals('pogonip', desc.nickname)
-    self.assertEquals('6DABD62BC65D4E6FE620293157FC76968DAB9C9B', desc.fingerprint)
-    self.assertEquals('75.5.248.48', desc.address)
+    self.assertEqual('pogonip', desc.nickname)
+    self.assertEqual('6DABD62BC65D4E6FE620293157FC76968DAB9C9B', desc.fingerprint)
+    self.assertEqual('75.5.248.48', desc.address)
 
     # the contact info block is huge so just checking the start and end,
     # including some of the embedded carriage returns
@@ -267,9 +267,9 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     descriptor_file = open(get_resource('negative_uptime'), 'rb')
     desc = next(stem.descriptor.parse_file(descriptor_file, 'server-descriptor 1.0'))
 
-    self.assertEquals('TipTor', desc.nickname)
-    self.assertEquals('137962D4931DBF08A24E843288B8A155D6D2AEDD', desc.fingerprint)
-    self.assertEquals('62.99.247.83', desc.address)
+    self.assertEqual('TipTor', desc.nickname)
+    self.assertEqual('137962D4931DBF08A24E843288B8A155D6D2AEDD', desc.fingerprint)
+    self.assertEqual('62.99.247.83', desc.address)
 
     # modify the relay version so it's after when the negative uptime bug
     # should appear
@@ -291,32 +291,32 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     ])
 
     desc = next(stem.descriptor.parse_file(descriptor_file, 'bridge-server-descriptor 1.0'))
-    self.assertEquals('Unnamed', desc.nickname)
-    self.assertEquals('AE54E28ED069CDF45F3009F963EE3B3D6FA26A2E', desc.fingerprint)
-    self.assertEquals('10.45.227.253', desc.address)
-    self.assertEquals(9001, desc.or_port)
-    self.assertEquals(None, desc.socks_port)
-    self.assertEquals(None, desc.dir_port)
-    self.assertEquals(b'Tor 0.2.3.12-alpha (git-800942b4176ca31c) on Linux x86_64', desc.platform)
-    self.assertEquals(stem.version.Version('0.2.3.12-alpha'), desc.tor_version)
-    self.assertEquals('Linux x86_64', desc.operating_system)
-    self.assertEquals(186, desc.uptime)
-    self.assertEquals(datetime.datetime(2012, 3, 22, 17, 34, 38), desc.published)
-    self.assertEquals(b'somebody', desc.contact)
-    self.assertEquals(['1', '2'], desc.link_protocols)
-    self.assertEquals(['1'], desc.circuit_protocols)
-    self.assertEquals(False, desc.hibernating)
-    self.assertEquals(False, desc.allow_single_hop_exits)
-    self.assertEquals(False, desc.extra_info_cache)
-    self.assertEquals('134F81F7A0D270B85FCD481DD10CEA34BA7B15C9', desc.extra_info_digest)
-    self.assertEquals(['2'], desc.hidden_service_dir)
-    self.assertEquals(expected_family, desc.family)
-    self.assertEquals(409600, desc.average_bandwidth)
-    self.assertEquals(819200, desc.burst_bandwidth)
-    self.assertEquals(5120, desc.observed_bandwidth)
-    self.assertEquals(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
-    self.assertEquals('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual('Unnamed', desc.nickname)
+    self.assertEqual('AE54E28ED069CDF45F3009F963EE3B3D6FA26A2E', desc.fingerprint)
+    self.assertEqual('10.45.227.253', desc.address)
+    self.assertEqual(9001, desc.or_port)
+    self.assertEqual(None, desc.socks_port)
+    self.assertEqual(None, desc.dir_port)
+    self.assertEqual(b'Tor 0.2.3.12-alpha (git-800942b4176ca31c) on Linux x86_64', desc.platform)
+    self.assertEqual(stem.version.Version('0.2.3.12-alpha'), desc.tor_version)
+    self.assertEqual('Linux x86_64', desc.operating_system)
+    self.assertEqual(186, desc.uptime)
+    self.assertEqual(datetime.datetime(2012, 3, 22, 17, 34, 38), desc.published)
+    self.assertEqual(b'somebody', desc.contact)
+    self.assertEqual(['1', '2'], desc.link_protocols)
+    self.assertEqual(['1'], desc.circuit_protocols)
+    self.assertEqual(False, desc.hibernating)
+    self.assertEqual(False, desc.allow_single_hop_exits)
+    self.assertEqual(False, desc.extra_info_cache)
+    self.assertEqual('134F81F7A0D270B85FCD481DD10CEA34BA7B15C9', desc.extra_info_digest)
+    self.assertEqual(['2'], desc.hidden_service_dir)
+    self.assertEqual(expected_family, desc.family)
+    self.assertEqual(409600, desc.average_bandwidth)
+    self.assertEqual(819200, desc.burst_bandwidth)
+    self.assertEqual(5120, desc.observed_bandwidth)
+    self.assertEqual(stem.exit_policy.ExitPolicy('reject *:*'), desc.exit_policy)
+    self.assertEqual('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
+    self.assertEqual([], desc.get_unrecognized_lines())
 
   def test_minimal_relay_descriptor(self):
     """
@@ -326,9 +326,9 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     desc = get_relay_server_descriptor()
 
-    self.assertEquals('caerSidi', desc.nickname)
-    self.assertEquals('71.35.133.197', desc.address)
-    self.assertEquals(None, desc.fingerprint)
+    self.assertEqual('caerSidi', desc.nickname)
+    self.assertEqual('71.35.133.197', desc.address)
+    self.assertEqual(None, desc.fingerprint)
     self.assertTrue(CRYPTO_BLOB in desc.onion_key)
 
   def test_with_opt(self):
@@ -337,7 +337,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     """
 
     desc = get_relay_server_descriptor({'opt': 'contact www.atagar.com/contact/'})
-    self.assertEquals(b'www.atagar.com/contact/', desc.contact)
+    self.assertEqual(b'www.atagar.com/contact/', desc.contact)
 
   def test_unrecognized_line(self):
     """
@@ -345,7 +345,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     """
 
     desc = get_relay_server_descriptor({'pepperjack': 'is oh so tasty!'})
-    self.assertEquals(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
+    self.assertEqual(['pepperjack is oh so tasty!'], desc.get_unrecognized_lines())
 
   def test_proceeding_line(self):
     """
@@ -426,12 +426,12 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     desc_text = get_relay_server_descriptor({'platform': ''}, content = True)
     desc = RelayDescriptor(desc_text, validate = False)
-    self.assertEquals(b'', desc.platform)
+    self.assertEqual(b'', desc.platform)
 
     # does the same but with 'platform ' replaced with 'platform'
     desc_text = desc_text.replace(b'platform ', b'platform')
     desc = RelayDescriptor(desc_text, validate = False)
-    self.assertEquals(b'', desc.platform)
+    self.assertEqual(b'', desc.platform)
 
   def test_platform_for_node_tor(self):
     """
@@ -439,9 +439,9 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     """
 
     desc = get_relay_server_descriptor({'platform': 'node-Tor 0.1.0 on Linux x86_64'})
-    self.assertEquals(b'node-Tor 0.1.0 on Linux x86_64', desc.platform)
-    self.assertEquals(stem.version.Version('0.1.0'), desc.tor_version)
-    self.assertEquals('Linux x86_64', desc.operating_system)
+    self.assertEqual(b'node-Tor 0.1.0 on Linux x86_64', desc.platform)
+    self.assertEqual(stem.version.Version('0.1.0'), desc.tor_version)
+    self.assertEqual('Linux x86_64', desc.operating_system)
 
   def test_protocols_no_circuit_versions(self):
     """
@@ -463,7 +463,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     desc_text = get_relay_server_descriptor({'published': '2012-02-29 04:03:19'}, content = True)
     expected_published = datetime.datetime(2012, 2, 29, 4, 3, 19)
-    self.assertEquals(expected_published, RelayDescriptor(desc_text).published)
+    self.assertEqual(expected_published, RelayDescriptor(desc_text).published)
 
   def test_published_no_time(self):
     """
@@ -492,9 +492,9 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
       expected_end = datetime.datetime(2005, 12, 16, 18, 0, 48)
       expected_values = [81, 8848, 8927, 8927, 83, 8848]
 
-      self.assertEquals(expected_end, attr[0])
-      self.assertEquals(900, attr[1])
-      self.assertEquals(expected_values, attr[2])
+      self.assertEqual(expected_end, attr[0])
+      self.assertEqual(900, attr[1])
+      self.assertEqual(expected_values, attr[2])
 
   def test_read_history_empty(self):
     """
@@ -503,9 +503,9 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     value = '2005-12-17 01:23:11 (900 s) '
     desc = get_relay_server_descriptor({'opt read-history': value})
-    self.assertEquals(datetime.datetime(2005, 12, 17, 1, 23, 11), desc.read_history_end)
-    self.assertEquals(900, desc.read_history_interval)
-    self.assertEquals([], desc.read_history_values)
+    self.assertEqual(datetime.datetime(2005, 12, 17, 1, 23, 11), desc.read_history_end)
+    self.assertEqual(900, desc.read_history_interval)
+    self.assertEqual([], desc.read_history_values)
 
   @patch('stem.descriptor.server_descriptor.RelayDescriptor._verify_digest', Mock())
   def test_annotations(self):
@@ -526,14 +526,14 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     desc_iter = stem.descriptor.server_descriptor._parse_file(io.BytesIO(desc_text))
 
     desc_entries = list(desc_iter)
-    self.assertEquals(1, len(desc_entries))
+    self.assertEqual(1, len(desc_entries))
     desc = desc_entries[0]
 
-    self.assertEquals('caerSidi', desc.nickname)
-    self.assertEquals(b'@pepperjack very tasty', desc.get_annotation_lines()[0])
-    self.assertEquals(b'@mushrooms not so much', desc.get_annotation_lines()[1])
-    self.assertEquals({b'@pepperjack': b'very tasty', b'@mushrooms': b'not so much'}, desc.get_annotations())
-    self.assertEquals([], desc.get_unrecognized_lines())
+    self.assertEqual('caerSidi', desc.nickname)
+    self.assertEqual(b'@pepperjack very tasty', desc.get_annotation_lines()[0])
+    self.assertEqual(b'@mushrooms not so much', desc.get_annotation_lines()[1])
+    self.assertEqual({b'@pepperjack': b'very tasty', b'@mushrooms': b'not so much'}, desc.get_annotations())
+    self.assertEqual([], desc.get_unrecognized_lines())
 
   def test_duplicate_field(self):
     """
@@ -558,11 +558,11 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
       # for one of them checks that the corresponding values are None
       if attr == 'router':
-        self.assertEquals(None, desc.nickname)
-        self.assertEquals(None, desc.address)
-        self.assertEquals(None, desc.or_port)
-        self.assertEquals(None, desc.socks_port)
-        self.assertEquals(None, desc.dir_port)
+        self.assertEqual(None, desc.nickname)
+        self.assertEqual(None, desc.address)
+        self.assertEqual(None, desc.or_port)
+        self.assertEqual(None, desc.socks_port)
+        self.assertEqual(None, desc.dir_port)
 
   def test_fingerprint_invalid(self):
     """
@@ -581,7 +581,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     expected = stem.exit_policy.MicroExitPolicy('accept 22-23,53,80,110')
     desc = get_relay_server_descriptor({'ipv6-policy': 'accept 22-23,53,80,110'})
-    self.assertEquals(expected, desc.exit_policy_v6)
+    self.assertEqual(expected, desc.exit_policy_v6)
 
   def test_ntor_onion_key(self):
     """
@@ -589,7 +589,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     """
 
     desc = get_relay_server_descriptor({'ntor-onion-key': 'Od2Sj3UXFyDjwESLXk6fhatqW9z/oBL/vAKJ+tbDqUU='})
-    self.assertEquals('Od2Sj3UXFyDjwESLXk6fhatqW9z/oBL/vAKJ+tbDqUU=', desc.ntor_onion_key)
+    self.assertEqual('Od2Sj3UXFyDjwESLXk6fhatqW9z/oBL/vAKJ+tbDqUU=', desc.ntor_onion_key)
 
   def test_minimal_bridge_descriptor(self):
     """
@@ -598,10 +598,10 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     desc = get_bridge_server_descriptor()
 
-    self.assertEquals('Unnamed', desc.nickname)
-    self.assertEquals('10.45.227.253', desc.address)
-    self.assertEquals(None, desc.fingerprint)
-    self.assertEquals('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
+    self.assertEqual('Unnamed', desc.nickname)
+    self.assertEqual('10.45.227.253', desc.address)
+    self.assertEqual(None, desc.fingerprint)
+    self.assertEqual('006FD96BA35E7785A6A3B8B75FE2E2435A13BDB4', desc.digest())
 
     # check that we don't have crypto fields
     self.assertRaises(AttributeError, getattr, desc, 'onion_key')
@@ -646,7 +646,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     router_digest = '068A2E28D4C934D9490303B7A645BA068DCA0504'
     desc = get_bridge_server_descriptor({'router-digest': router_digest})
-    self.assertEquals(router_digest, desc.digest())
+    self.assertEqual(router_digest, desc.digest())
 
     # checks when missing
 
@@ -655,7 +655,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
 
     # check that we can still construct it without validation
     desc = BridgeDescriptor(desc_text, validate = False)
-    self.assertEquals(None, desc.digest())
+    self.assertEqual(None, desc.digest())
 
     # checks with invalid content
 
@@ -671,7 +671,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
       self.assertRaises(ValueError, BridgeDescriptor, desc_text)
 
       desc = BridgeDescriptor(desc_text, validate = False)
-      self.assertEquals(value, desc.digest())
+      self.assertEqual(value, desc.digest())
 
   def test_or_address_v4(self):
     """
@@ -679,7 +679,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     """
 
     desc = get_bridge_server_descriptor({'or-address': '10.45.227.253:9001'})
-    self.assertEquals([('10.45.227.253', 9001, False)], desc.or_addresses)
+    self.assertEqual([('10.45.227.253', 9001, False)], desc.or_addresses)
 
   def test_or_address_v6(self):
     """
@@ -687,7 +687,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     """
 
     desc = get_bridge_server_descriptor({'or-address': '[fd9f:2e19:3bcf::02:9970]:9001'})
-    self.assertEquals([('fd9f:2e19:3bcf::02:9970', 9001, True)], desc.or_addresses)
+    self.assertEqual([('fd9f:2e19:3bcf::02:9970', 9001, True)], desc.or_addresses)
 
   def test_or_address_multiple(self):
     """
@@ -704,7 +704,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
     ]
 
     desc = BridgeDescriptor(desc_text)
-    self.assertEquals(expected_or_addresses, desc.or_addresses)
+    self.assertEqual(expected_or_addresses, desc.or_addresses)
 
   def _expect_invalid_attr(self, desc_text, attr = None, expected_value = None):
     """
@@ -720,7 +720,7 @@ Qlx9HNCqCY877ztFRC624ja2ql6A2hBcuoYMbkHjcQ4=
       # check that the invalid attribute matches the expected value when
       # constructed without validation
 
-      self.assertEquals(expected_value, getattr(desc, attr))
+      self.assertEqual(expected_value, getattr(desc, attr))
     else:
       # check a default attribute
-      self.assertEquals('caerSidi', desc.nickname)
+      self.assertEqual('caerSidi', desc.nickname)
diff --git a/test/unit/descriptor/tordnsel.py b/test/unit/descriptor/tordnsel.py
index 220af26..26227cc 100644
--- a/test/unit/descriptor/tordnsel.py
+++ b/test/unit/descriptor/tordnsel.py
@@ -82,7 +82,7 @@ class TestTorDNSELDescriptor(unittest.TestCase):
 
     # skip exit address if malformed date and validate is False
 
-    desc = _parse_file(io.BytesIO(MALFORMED_ENTRY_2), validate=False).next()
+    desc = next(_parse_file(io.BytesIO(MALFORMED_ENTRY_2), validate=False))
     self.assertTrue(is_valid_fingerprint(desc.fingerprint))
     self.assertEqual('030B22437D99B2DB2908B747B6962EAD13AB4038', desc.fingerprint)
     self.assertEqual(0, len(desc.exit_addresses))
diff --git a/test/unit/exit_policy/policy.py b/test/unit/exit_policy/policy.py
index 406ef62..3eef79b 100644
--- a/test/unit/exit_policy/policy.py
+++ b/test/unit/exit_policy/policy.py
@@ -18,8 +18,8 @@ class TestExitPolicy(unittest.TestCase):
   def test_example(self):
     # tests the ExitPolicy and MicroExitPolicy pydoc examples
     policy = ExitPolicy('accept *:80', 'accept *:443', 'reject *:*')
-    self.assertEquals('accept *:80, accept *:443, reject *:*', str(policy))
-    self.assertEquals('accept 80, 443', policy.summary())
+    self.assertEqual('accept *:80, accept *:443, reject *:*', str(policy))
+    self.assertEqual('accept 80, 443', policy.summary())
     self.assertTrue(policy.can_exit_to('75.119.206.243', 80))
 
     policy = MicroExitPolicy('accept 80,443')
@@ -36,20 +36,20 @@ class TestExitPolicy(unittest.TestCase):
     )
 
     policy = ExitPolicy('accept *:80', 'accept *:443', 'reject *:*')
-    self.assertEquals(expected_policy, policy)
+    self.assertEqual(expected_policy, policy)
 
     policy = ExitPolicy(*'accept *:80, accept *:443, reject *:*'.split(','))
-    self.assertEquals(expected_policy, policy)
+    self.assertEqual(expected_policy, policy)
 
     # checks that we truncate after getting a catch-all policy
 
     policy = ExitPolicy(*'accept *:80, accept *:443, reject *:*, accept *:20-50'.split(','))
-    self.assertEquals(expected_policy, policy)
+    self.assertEqual(expected_policy, policy)
 
     # checks that we compress redundant policies
 
     policy = ExitPolicy(*'reject *:80, reject *:443, reject *:*'.split(','))
-    self.assertEquals(ExitPolicy('reject *:*'), policy)
+    self.assertEqual(ExitPolicy('reject *:*'), policy)
 
   def test_can_exit_to(self):
     # Basic sanity test for our can_exit_to() method. Most of the interesting
@@ -58,12 +58,12 @@ class TestExitPolicy(unittest.TestCase):
 
     policy = ExitPolicy('accept *:80', 'accept *:443', 'reject *:*')
 
-    for index in xrange(1, 500):
+    for index in range(1, 500):
       ip_addr = '%i.%i.%i.%i' % (index / 2, index / 2, index / 2, index / 2)
       expected_result = index in (80, 443)
 
-      self.assertEquals(expected_result, policy.can_exit_to(ip_addr, index))
-      self.assertEquals(expected_result, policy.can_exit_to(port = index))
+      self.assertEqual(expected_result, policy.can_exit_to(ip_addr, index))
+      self.assertEqual(expected_result, policy.can_exit_to(port = index))
 
   def test_is_exiting_allowed(self):
     test_inputs = {
@@ -78,24 +78,24 @@ class TestExitPolicy(unittest.TestCase):
       ('reject 127.0.0.1:*', 'accept *:80', 'reject *:*'): True,
     }
 
-    for rules, expected_result in test_inputs.items():
+    for rules, expected_result in list(test_inputs.items()):
       policy = ExitPolicy(*rules)
-      self.assertEquals(expected_result, policy.is_exiting_allowed())
+      self.assertEqual(expected_result, policy.is_exiting_allowed())
 
   def test_summary_examples(self):
     # checks the summary() method's pydoc examples
 
     policy = ExitPolicy('accept *:80', 'accept *:443', 'reject *:*')
-    self.assertEquals('accept 80, 443', policy.summary())
+    self.assertEqual('accept 80, 443', policy.summary())
 
     policy = ExitPolicy('accept *:443', 'reject *:1-1024', 'accept *:*')
-    self.assertEquals('reject 1-442, 444-1024', policy.summary())
+    self.assertEqual('reject 1-442, 444-1024', policy.summary())
 
   def test_summary_large_ranges(self):
     # checks the summary() method when the policy includes very large port ranges
 
     policy = ExitPolicy('reject *:80-65535', 'accept *:1-65533', 'reject *:*')
-    self.assertEquals('accept 1-79', policy.summary())
+    self.assertEqual('accept 1-79', policy.summary())
 
   def test_non_private_non_default_policy(self):
     policy = get_config_policy('reject *:80-65535, accept *:1-65533, reject *:*')
@@ -154,10 +154,10 @@ class TestExitPolicy(unittest.TestCase):
     # sanity test for our __str__ method
 
     policy = ExitPolicy('  accept *:80\n', '\taccept *:443')
-    self.assertEquals('accept *:80, accept *:443', str(policy))
+    self.assertEqual('accept *:80, accept *:443', str(policy))
 
     policy = ExitPolicy('reject 0.0.0.0/255.255.255.0:*', 'accept *:*')
-    self.assertEquals('reject 0.0.0.0/24:*, accept *:*', str(policy))
+    self.assertEqual('reject 0.0.0.0/24:*, accept *:*', str(policy))
 
   def test_iter(self):
     # sanity test for our __iter__ method
@@ -168,8 +168,8 @@ class TestExitPolicy(unittest.TestCase):
       ExitPolicyRule('reject *:*'),
     ]
 
-    self.assertEquals(rules, list(ExitPolicy(*rules)))
-    self.assertEquals(rules, list(ExitPolicy('accept *:80', 'accept *:443', 'reject *:*')))
+    self.assertEqual(rules, list(ExitPolicy(*rules)))
+    self.assertEqual(rules, list(ExitPolicy('accept *:80', 'accept *:443', 'reject *:*')))
 
   def test_microdescriptor_parsing(self):
     # mapping between inputs and if they should succeed or not
@@ -190,7 +190,7 @@ class TestExitPolicy(unittest.TestCase):
       'bar 80,443': False,
     }
 
-    for policy_arg, expect_success in test_inputs.items():
+    for policy_arg, expect_success in list(test_inputs.items()):
       try:
         policy = MicroExitPolicy(policy_arg)
 
@@ -225,10 +225,10 @@ class TestExitPolicy(unittest.TestCase):
       'reject 1-1024': {1: False, 1024: False, 1025: True},
     }
 
-    for policy_arg, attr in test_inputs.items():
+    for policy_arg, attr in list(test_inputs.items()):
       policy = MicroExitPolicy(policy_arg)
 
-      for port, expected_value in attr.items():
+      for port, expected_value in list(attr.items()):
         self.assertEqual(expected_value, policy.can_exit_to(port = port))
 
     # address argument should be ignored
@@ -261,7 +261,7 @@ class TestExitPolicy(unittest.TestCase):
       ),
     }
 
-    for test_input, expected in test_inputs.items():
+    for test_input, expected in list(test_inputs.items()):
       self.assertEqual(expected, get_config_policy(test_input, '12.34.56.78'))
 
     test_inputs = (
diff --git a/test/unit/exit_policy/rule.py b/test/unit/exit_policy/rule.py
index 5a6c0d4..39b6de9 100644
--- a/test/unit/exit_policy/rule.py
+++ b/test/unit/exit_policy/rule.py
@@ -47,7 +47,7 @@ class TestExitPolicyRule(unittest.TestCase):
 
     for rule_arg in test_inputs:
       rule = ExitPolicyRule(rule_arg)
-      self.assertEquals(rule_arg, str(rule))
+      self.assertEqual(rule_arg, str(rule))
 
   def test_str_changed(self):
     # some instances where our rule is valid but won't match our str() representation
@@ -58,9 +58,9 @@ class TestExitPolicyRule(unittest.TestCase):
       'accept [::]/128:*': 'accept [0000:0000:0000:0000:0000:0000:0000:0000]:*',
     }
 
-    for rule_arg, expected_str in test_inputs.items():
+    for rule_arg, expected_str in list(test_inputs.items()):
       rule = ExitPolicyRule(rule_arg)
-      self.assertEquals(expected_str, str(rule))
+      self.assertEqual(expected_str, str(rule))
 
   def test_valid_wildcard(self):
     test_inputs = {
@@ -83,22 +83,22 @@ class TestExitPolicyRule(unittest.TestCase):
       'accept 192.168.0.1:1-65534': (False, False),
     }
 
-    for rule_arg, attr in test_inputs.items():
+    for rule_arg, attr in list(test_inputs.items()):
       is_address_wildcard, is_port_wildcard = attr
 
       rule = ExitPolicyRule(rule_arg)
-      self.assertEquals(is_address_wildcard, rule.is_address_wildcard())
-      self.assertEquals(is_port_wildcard, rule.is_port_wildcard())
+      self.assertEqual(is_address_wildcard, rule.is_address_wildcard())
+      self.assertEqual(is_port_wildcard, rule.is_port_wildcard())
 
     # check that when appropriate a /0 is reported as *not* being a wildcard
 
     rule = ExitPolicyRule('reject 127.0.0.1/0:*')
     rule._submask_wildcard = False
-    self.assertEquals(False, rule.is_address_wildcard())
+    self.assertEqual(False, rule.is_address_wildcard())
 
     rule = ExitPolicyRule('reject [0000:0000:0000:0000:0000:0000:0000:0000]/0:80')
     rule._submask_wildcard = False
-    self.assertEquals(False, rule.is_address_wildcard())
+    self.assertEqual(False, rule.is_address_wildcard())
 
   def test_invalid_wildcard(self):
     test_inputs = (
@@ -113,12 +113,12 @@ class TestExitPolicyRule(unittest.TestCase):
 
   def test_wildcard_attributes(self):
     rule = ExitPolicyRule('reject *:*')
-    self.assertEquals(AddressType.WILDCARD, rule.get_address_type())
-    self.assertEquals(None, rule.address)
-    self.assertEquals(None, rule.get_mask())
-    self.assertEquals(None, rule.get_masked_bits())
-    self.assertEquals(1, rule.min_port)
-    self.assertEquals(65535, rule.max_port)
+    self.assertEqual(AddressType.WILDCARD, rule.get_address_type())
+    self.assertEqual(None, rule.address)
+    self.assertEqual(None, rule.get_mask())
+    self.assertEqual(None, rule.get_masked_bits())
+    self.assertEqual(1, rule.min_port)
+    self.assertEqual(65535, rule.max_port)
 
   def test_valid_ipv4_addresses(self):
     test_inputs = {
@@ -128,14 +128,14 @@ class TestExitPolicyRule(unittest.TestCase):
       '255.255.255.255/0': ('255.255.255.255', '0.0.0.0', 0),
     }
 
-    for rule_addr, attr in test_inputs.items():
+    for rule_addr, attr in list(test_inputs.items()):
       address, mask, masked_bits = attr
 
       rule = ExitPolicyRule('accept %s:*' % rule_addr)
-      self.assertEquals(AddressType.IPv4, rule.get_address_type())
-      self.assertEquals(address, rule.address)
-      self.assertEquals(mask, rule.get_mask())
-      self.assertEquals(masked_bits, rule.get_masked_bits())
+      self.assertEqual(AddressType.IPv4, rule.get_address_type())
+      self.assertEqual(address, rule.address)
+      self.assertEqual(mask, rule.get_mask())
+      self.assertEqual(masked_bits, rule.get_masked_bits())
 
   def test_invalid_ipv4_addresses(self):
     test_inputs = (
@@ -167,14 +167,14 @@ class TestExitPolicyRule(unittest.TestCase):
          'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', 128),
     }
 
-    for rule_addr, attr in test_inputs.items():
+    for rule_addr, attr in list(test_inputs.items()):
       address, mask, masked_bits = attr
 
       rule = ExitPolicyRule('accept %s:*' % rule_addr)
-      self.assertEquals(AddressType.IPv6, rule.get_address_type())
-      self.assertEquals(address, rule.address)
-      self.assertEquals(mask, rule.get_mask())
-      self.assertEquals(masked_bits, rule.get_masked_bits())
+      self.assertEqual(AddressType.IPv6, rule.get_address_type())
+      self.assertEqual(address, rule.address)
+      self.assertEqual(mask, rule.get_mask())
+      self.assertEqual(masked_bits, rule.get_masked_bits())
 
   def test_invalid_ipv6_addresses(self):
     test_inputs = (
@@ -199,12 +199,12 @@ class TestExitPolicyRule(unittest.TestCase):
       '80-443': (80, 443),
     }
 
-    for rule_port, attr in test_inputs.items():
+    for rule_port, attr in list(test_inputs.items()):
       min_port, max_port = attr
 
       rule = ExitPolicyRule('accept 127.0.0.1:%s' % rule_port)
-      self.assertEquals(min_port, rule.min_port)
-      self.assertEquals(max_port, rule.max_port)
+      self.assertEqual(min_port, rule.min_port)
+      self.assertEqual(max_port, rule.max_port)
 
   def test_invalid_ports(self):
     test_inputs = (
@@ -246,12 +246,12 @@ class TestExitPolicyRule(unittest.TestCase):
       },
     }
 
-    for rule_arg, matches in test_inputs.items():
+    for rule_arg, matches in list(test_inputs.items()):
       rule = ExitPolicyRule(rule_arg)
       rule._submask_wildcard = False
 
-      for match_args, expected_result in matches.items():
-        self.assertEquals(expected_result, rule.is_match(*match_args))
+      for match_args, expected_result in list(matches.items()):
+        self.assertEqual(expected_result, rule.is_match(*match_args))
 
     # port zero is special in that exit policies can include it, but it's not
     # something that we can match against
@@ -282,11 +282,11 @@ class TestExitPolicyRule(unittest.TestCase):
       },
     }
 
-    for rule_arg, matches in test_inputs.items():
+    for rule_arg, matches in list(test_inputs.items()):
       rule = ExitPolicyRule(rule_arg)
 
-      for match_args, expected_result in matches.items():
-        self.assertEquals(expected_result, rule.is_match(*match_args))
+      for match_args, expected_result in list(matches.items()):
+        self.assertEqual(expected_result, rule.is_match(*match_args))
 
   def test_is_match_ipv6(self):
     test_inputs = {
@@ -313,11 +313,11 @@ class TestExitPolicyRule(unittest.TestCase):
       },
     }
 
-    for rule_arg, matches in test_inputs.items():
+    for rule_arg, matches in list(test_inputs.items()):
       rule = ExitPolicyRule(rule_arg)
 
-      for match_args, expected_result in matches.items():
-        self.assertEquals(expected_result, rule.is_match(*match_args))
+      for match_args, expected_result in list(matches.items()):
+        self.assertEqual(expected_result, rule.is_match(*match_args))
 
   def test_is_match_port(self):
     test_inputs = {
@@ -341,8 +341,8 @@ class TestExitPolicyRule(unittest.TestCase):
       },
     }
 
-    for rule_arg, matches in test_inputs.items():
+    for rule_arg, matches in list(test_inputs.items()):
       rule = ExitPolicyRule(rule_arg)
 
-      for match_args, expected_result in matches.items():
-        self.assertEquals(expected_result, rule.is_match(*match_args))
+      for match_args, expected_result in list(matches.items()):
+        self.assertEqual(expected_result, rule.is_match(*match_args))
diff --git a/test/unit/response/authchallenge.py b/test/unit/response/authchallenge.py
index aa71ecc..2185ea8 100644
--- a/test/unit/response/authchallenge.py
+++ b/test/unit/response/authchallenge.py
@@ -45,7 +45,7 @@ class TestAuthChallengeResponse(unittest.TestCase):
 
     auth_challenge_comp = VALID_RESPONSE.split()
 
-    for index in xrange(1, len(auth_challenge_comp)):
+    for index in range(1, len(auth_challenge_comp)):
       # Attempts to parse a message without this item. The first item is
       # skipped because, without the 250 code, the message won't be
       # constructed.
diff --git a/test/unit/response/control_line.py b/test/unit/response/control_line.py
index edccb2e..adcadf3 100644
--- a/test/unit/response/control_line.py
+++ b/test/unit/response/control_line.py
@@ -22,12 +22,12 @@ class TestControlLine(unittest.TestCase):
     """
 
     line = stem.response.ControlLine("\"We're all mad here.\" says the grinning cat.")
-    self.assertEquals(line.pop(True), "We're all mad here.")
-    self.assertEquals(line.pop(), 'says')
-    self.assertEquals(line.remainder(), 'the grinning cat.')
+    self.assertEqual(line.pop(True), "We're all mad here.")
+    self.assertEqual(line.pop(), 'says')
+    self.assertEqual(line.remainder(), 'the grinning cat.')
 
     line = stem.response.ControlLine('"this has a \\" and \\\\ in it" foo=bar more_data')
-    self.assertEquals(line.pop(True, True), 'this has a " and \\ in it')
+    self.assertEqual(line.pop(True, True), 'this has a " and \\ in it')
 
   def test_string(self):
     """
@@ -35,12 +35,12 @@ class TestControlLine(unittest.TestCase):
     """
 
     line = stem.response.ControlLine(PROTOCOLINFO_RESPONSE[0])
-    self.assertEquals(line, 'PROTOCOLINFO 1')
+    self.assertEqual(line, 'PROTOCOLINFO 1')
     self.assertTrue(line.startswith('PROTOCOLINFO '))
 
     # checks that popping items doesn't effect us
     line.pop()
-    self.assertEquals(line, 'PROTOCOLINFO 1')
+    self.assertEqual(line, 'PROTOCOLINFO 1')
     self.assertTrue(line.startswith('PROTOCOLINFO '))
 
   def test_general_usage(self):
@@ -50,35 +50,35 @@ class TestControlLine(unittest.TestCase):
 
     # pops a series of basic, space separated entries
     line = stem.response.ControlLine(PROTOCOLINFO_RESPONSE[0])
-    self.assertEquals(line.remainder(), 'PROTOCOLINFO 1')
+    self.assertEqual(line.remainder(), 'PROTOCOLINFO 1')
     self.assertFalse(line.is_empty())
     self.assertFalse(line.is_next_quoted())
     self.assertFalse(line.is_next_mapping())
-    self.assertEquals(None, line.peek_key())
+    self.assertEqual(None, line.peek_key())
 
     self.assertRaises(ValueError, line.pop_mapping)
-    self.assertEquals(line.pop(), 'PROTOCOLINFO')
-    self.assertEquals(line.remainder(), '1')
+    self.assertEqual(line.pop(), 'PROTOCOLINFO')
+    self.assertEqual(line.remainder(), '1')
     self.assertFalse(line.is_empty())
     self.assertFalse(line.is_next_quoted())
     self.assertFalse(line.is_next_mapping())
-    self.assertEquals(None, line.peek_key())
+    self.assertEqual(None, line.peek_key())
 
     self.assertRaises(ValueError, line.pop_mapping)
-    self.assertEquals(line.pop(), '1')
-    self.assertEquals(line.remainder(), '')
+    self.assertEqual(line.pop(), '1')
+    self.assertEqual(line.remainder(), '')
     self.assertTrue(line.is_empty())
     self.assertFalse(line.is_next_quoted())
     self.assertFalse(line.is_next_mapping())
-    self.assertEquals(None, line.peek_key())
+    self.assertEqual(None, line.peek_key())
 
     self.assertRaises(IndexError, line.pop_mapping)
     self.assertRaises(IndexError, line.pop)
-    self.assertEquals(line.remainder(), '')
+    self.assertEqual(line.remainder(), '')
     self.assertTrue(line.is_empty())
     self.assertFalse(line.is_next_quoted())
     self.assertFalse(line.is_next_mapping())
-    self.assertEquals(None, line.peek_key())
+    self.assertEqual(None, line.peek_key())
 
   def test_pop_mapping(self):
     """
@@ -89,32 +89,32 @@ class TestControlLine(unittest.TestCase):
     version_entry = 'Tor="0.2.1.30 (0a083b0188cacd2f07838ff0446113bd5211a024)"'
 
     line = stem.response.ControlLine(version_entry)
-    self.assertEquals(line.remainder(), version_entry)
+    self.assertEqual(line.remainder(), version_entry)
     self.assertFalse(line.is_empty())
     self.assertFalse(line.is_next_quoted())
     self.assertTrue(line.is_next_mapping())
     self.assertTrue(line.is_next_mapping(key = 'Tor'))
     self.assertTrue(line.is_next_mapping(key = 'Tor', quoted = True))
     self.assertTrue(line.is_next_mapping(quoted = True))
-    self.assertEquals('Tor', line.peek_key())
+    self.assertEqual('Tor', line.peek_key())
 
     # try popping this as a non-quoted mapping
-    self.assertEquals(line.pop_mapping(), ('Tor', '"0.2.1.30'))
-    self.assertEquals(line.remainder(), '(0a083b0188cacd2f07838ff0446113bd5211a024)"')
+    self.assertEqual(line.pop_mapping(), ('Tor', '"0.2.1.30'))
+    self.assertEqual(line.remainder(), '(0a083b0188cacd2f07838ff0446113bd5211a024)"')
     self.assertFalse(line.is_empty())
     self.assertFalse(line.is_next_quoted())
     self.assertFalse(line.is_next_mapping())
     self.assertRaises(ValueError, line.pop_mapping)
-    self.assertEquals(None, line.peek_key())
+    self.assertEqual(None, line.peek_key())
 
     # try popping this as a quoted mapping
     line = stem.response.ControlLine(version_entry)
-    self.assertEquals(line.pop_mapping(True), ('Tor', '0.2.1.30 (0a083b0188cacd2f07838ff0446113bd5211a024)'))
-    self.assertEquals(line.remainder(), '')
+    self.assertEqual(line.pop_mapping(True), ('Tor', '0.2.1.30 (0a083b0188cacd2f07838ff0446113bd5211a024)'))
+    self.assertEqual(line.remainder(), '')
     self.assertTrue(line.is_empty())
     self.assertFalse(line.is_next_quoted())
     self.assertFalse(line.is_next_mapping())
-    self.assertEquals(None, line.peek_key())
+    self.assertEqual(None, line.peek_key())
 
   def test_escapes(self):
     """
@@ -125,13 +125,13 @@ class TestControlLine(unittest.TestCase):
 
     auth_line = PROTOCOLINFO_RESPONSE[1]
     line = stem.response.ControlLine(auth_line)
-    self.assertEquals(line, auth_line)
-    self.assertEquals(line.remainder(), auth_line)
+    self.assertEqual(line, auth_line)
+    self.assertEqual(line.remainder(), auth_line)
 
-    self.assertEquals(line.pop(), 'AUTH')
-    self.assertEquals(line.pop_mapping(), ('METHODS', 'COOKIE'))
+    self.assertEqual(line.pop(), 'AUTH')
+    self.assertEqual(line.pop_mapping(), ('METHODS', 'COOKIE'))
 
-    self.assertEquals(line.remainder(), r'COOKIEFILE="/tmp/my data\\\"dir//control_auth_cookie"')
+    self.assertEqual(line.remainder(), r'COOKIEFILE="/tmp/my data\\\"dir//control_auth_cookie"')
     self.assertTrue(line.is_next_mapping())
     self.assertTrue(line.is_next_mapping(key = 'COOKIEFILE'))
     self.assertTrue(line.is_next_mapping(quoted = True))
@@ -139,36 +139,36 @@ class TestControlLine(unittest.TestCase):
     cookie_file_entry = line.remainder()
 
     # try a general pop
-    self.assertEquals(line.pop(), 'COOKIEFILE="/tmp/my')
-    self.assertEquals(line.pop(), r'data\\\"dir//control_auth_cookie"')
+    self.assertEqual(line.pop(), 'COOKIEFILE="/tmp/my')
+    self.assertEqual(line.pop(), r'data\\\"dir//control_auth_cookie"')
     self.assertTrue(line.is_empty())
 
     # try a general pop with escapes
     line = stem.response.ControlLine(cookie_file_entry)
-    self.assertEquals(line.pop(escaped = True), 'COOKIEFILE="/tmp/my')
-    self.assertEquals(line.pop(escaped = True), r'data\"dir//control_auth_cookie"')
+    self.assertEqual(line.pop(escaped = True), 'COOKIEFILE="/tmp/my')
+    self.assertEqual(line.pop(escaped = True), r'data\"dir//control_auth_cookie"')
     self.assertTrue(line.is_empty())
 
     # try a mapping pop
     line = stem.response.ControlLine(cookie_file_entry)
-    self.assertEquals(line.pop_mapping(), ('COOKIEFILE', '"/tmp/my'))
-    self.assertEquals(line.remainder(), r'data\\\"dir//control_auth_cookie"')
+    self.assertEqual(line.pop_mapping(), ('COOKIEFILE', '"/tmp/my'))
+    self.assertEqual(line.remainder(), r'data\\\"dir//control_auth_cookie"')
     self.assertFalse(line.is_empty())
 
     # try a quoted mapping pop (this should trip up on the escaped quote)
     line = stem.response.ControlLine(cookie_file_entry)
-    self.assertEquals(line.pop_mapping(True), ('COOKIEFILE', '/tmp/my data\\\\\\'))
-    self.assertEquals(line.remainder(), 'dir//control_auth_cookie"')
+    self.assertEqual(line.pop_mapping(True), ('COOKIEFILE', '/tmp/my data\\\\\\'))
+    self.assertEqual(line.remainder(), 'dir//control_auth_cookie"')
     self.assertFalse(line.is_empty())
 
     # try an escaped quoted mapping pop
     line = stem.response.ControlLine(cookie_file_entry)
-    self.assertEquals(line.pop_mapping(True, True), ('COOKIEFILE', r'/tmp/my data\"dir//control_auth_cookie'))
+    self.assertEqual(line.pop_mapping(True, True), ('COOKIEFILE', r'/tmp/my data\"dir//control_auth_cookie'))
     self.assertTrue(line.is_empty())
 
     # try an escaped slash followed by a character that could be part of an
     # escape sequence
 
     line = stem.response.ControlLine(r'COOKIEFILE="C:\\Users\\Atagar\\AppData\\tor\\control_auth_cookie"')
-    self.assertEquals(line.pop_mapping(True, True), ('COOKIEFILE', r'C:\Users\Atagar\AppData\tor\control_auth_cookie'))
+    self.assertEqual(line.pop_mapping(True, True), ('COOKIEFILE', r'C:\Users\Atagar\AppData\tor\control_auth_cookie'))
     self.assertTrue(line.is_empty())
diff --git a/test/unit/response/control_message.py b/test/unit/response/control_message.py
index 91b7fbd..c0fb2f3 100644
--- a/test/unit/response/control_message.py
+++ b/test/unit/response/control_message.py
@@ -3,9 +3,13 @@ Unit tests for the stem.response.ControlMessage parsing and class.
 """
 
 import socket
-import StringIO
 import unittest
 
+try:
+  from StringIO import StringIO
+except:
+  from io import StringIO
+
 import stem.socket
 import stem.response
 import stem.response.getinfo
@@ -50,11 +54,11 @@ class TestControlMessage(unittest.TestCase):
     """
 
     message = self._assert_message_parses(OK_REPLY)
-    self.assertEquals('OK', str(message))
+    self.assertEqual('OK', str(message))
 
     contents = message.content()
-    self.assertEquals(1, len(contents))
-    self.assertEquals(('250', ' ', 'OK'), contents[0])
+    self.assertEqual(1, len(contents))
+    self.assertEqual(('250', ' ', 'OK'), contents[0])
 
   def test_event_response(self):
     """
@@ -63,20 +67,20 @@ class TestControlMessage(unittest.TestCase):
 
     # BW event
     message = self._assert_message_parses(EVENT_BW)
-    self.assertEquals('BW 32326 2856', str(message))
+    self.assertEqual('BW 32326 2856', str(message))
 
     contents = message.content()
-    self.assertEquals(1, len(contents))
-    self.assertEquals(('650', ' ', 'BW 32326 2856'), contents[0])
+    self.assertEqual(1, len(contents))
+    self.assertEqual(('650', ' ', 'BW 32326 2856'), contents[0])
 
     # few types of CIRC events
     for circ_content in (EVENT_CIRC_TIMEOUT, EVENT_CIRC_LAUNCHED, EVENT_CIRC_EXTENDED):
       message = self._assert_message_parses(circ_content)
-      self.assertEquals(circ_content[4:-2], str(message))
+      self.assertEqual(circ_content[4:-2], str(message))
 
       contents = message.content()
-      self.assertEquals(1, len(contents))
-      self.assertEquals(('650', ' ', str(message)), contents[0])
+      self.assertEqual(1, len(contents))
+      self.assertEqual(('650', ' ', str(message)), contents[0])
 
   def test_getinfo_response(self):
     """
@@ -85,27 +89,27 @@ class TestControlMessage(unittest.TestCase):
 
     # GETINFO version (basic single-line results)
     message = self._assert_message_parses(GETINFO_VERSION)
-    self.assertEquals(2, len(list(message)))
-    self.assertEquals(2, len(str(message).splitlines()))
+    self.assertEqual(2, len(list(message)))
+    self.assertEqual(2, len(str(message).splitlines()))
 
     # manually checks the contents
     contents = message.content()
-    self.assertEquals(2, len(contents))
-    self.assertEquals(('250', '-', 'version=0.2.2.23-alpha (git-b85eb949b528f4d7)'), contents[0])
-    self.assertEquals(('250', ' ', 'OK'), contents[1])
+    self.assertEqual(2, len(contents))
+    self.assertEqual(('250', '-', 'version=0.2.2.23-alpha (git-b85eb949b528f4d7)'), contents[0])
+    self.assertEqual(('250', ' ', 'OK'), contents[1])
 
     # GETINFO info/names (data entry)
     message = self._assert_message_parses(GETINFO_INFONAMES)
-    self.assertEquals(2, len(list(message)))
-    self.assertEquals(8, len(str(message).splitlines()))
+    self.assertEqual(2, len(list(message)))
+    self.assertEqual(8, len(str(message).splitlines()))
 
     # manually checks the contents
     contents = message.content()
-    self.assertEquals(2, len(contents))
+    self.assertEqual(2, len(contents))
 
     first_entry = (contents[0][0], contents[0][1], contents[0][2][:contents[0][2].find('\n')])
-    self.assertEquals(('250', '+', 'info/names='), first_entry)
-    self.assertEquals(('250', ' ', 'OK'), contents[1])
+    self.assertEqual(('250', '+', 'info/names='), first_entry)
+    self.assertEqual(('250', ' ', 'OK'), contents[1])
 
   def test_no_crlf(self):
     """
@@ -124,7 +128,7 @@ class TestControlMessage(unittest.TestCase):
     for index, line in enumerate(infonames_lines):
       # replace the CRLF for the line
       infonames_lines[index] = line.rstrip('\r\n') + '\n'
-      test_socket_file = StringIO.StringIO(''.join(infonames_lines))
+      test_socket_file = StringIO(''.join(infonames_lines))
       self.assertRaises(stem.ProtocolError, stem.socket.recv_message, test_socket_file)
 
       # puts the CRLF back
@@ -150,8 +154,8 @@ class TestControlMessage(unittest.TestCase):
         # - this is part of the message prefix
         # - this is disrupting the line ending
 
-        self.assertRaises(stem.ProtocolError, stem.socket.recv_message, StringIO.StringIO(removal_test_input))
-        self.assertRaises(stem.ProtocolError, stem.socket.recv_message, StringIO.StringIO(replacement_test_input))
+        self.assertRaises(stem.ProtocolError, stem.socket.recv_message, StringIO(removal_test_input))
+        self.assertRaises(stem.ProtocolError, stem.socket.recv_message, StringIO(replacement_test_input))
       else:
         # otherwise the data will be malformed, but this goes undetected
         self._assert_message_parses(removal_test_input)
@@ -175,7 +179,7 @@ class TestControlMessage(unittest.TestCase):
       stem.response.ControlMessage for the given input
     """
 
-    message = stem.socket.recv_message(StringIO.StringIO(controller_reply))
+    message = stem.socket.recv_message(StringIO(controller_reply))
 
     # checks that the raw_content equals the input value
     self.assertEqual(controller_reply, message.raw_content())
diff --git a/test/unit/response/events.py b/test/unit/response/events.py
index 054a9ff..2e5ef50 100644
--- a/test/unit/response/events.py
+++ b/test/unit/response/events.py
@@ -395,7 +395,7 @@ class TestEvents(unittest.TestCase):
       self.assertEqual('sent: 25, received: 15', msg)
 
     def event_sender():
-      for index in xrange(3):
+      for index in range(3):
         print_bw(_get_event('650 BW 15 25'))
         time.sleep(0.05)
 
diff --git a/test/unit/response/protocolinfo.py b/test/unit/response/protocolinfo.py
index fa87a0d..4dcf291 100644
--- a/test/unit/response/protocolinfo.py
+++ b/test/unit/response/protocolinfo.py
@@ -72,7 +72,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
 
     # exercise some of the ControlMessage functionality
     raw_content = (NO_AUTH + '\n').replace('\n', '\r\n')
-    self.assertEquals(raw_content, control_message.raw_content())
+    self.assertEqual(raw_content, control_message.raw_content())
     self.assertTrue(str(control_message).startswith('PROTOCOLINFO 1'))
 
     # attempt to convert the wrong type
@@ -90,11 +90,11 @@ class TestProtocolInfoResponse(unittest.TestCase):
     control_message = mocking.get_message(NO_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
 
-    self.assertEquals(1, control_message.protocol_version)
-    self.assertEquals(stem.version.Version('0.2.1.30'), control_message.tor_version)
-    self.assertEquals((AuthMethod.NONE, ), control_message.auth_methods)
-    self.assertEquals((), control_message.unknown_auth_methods)
-    self.assertEquals(None, control_message.cookie_path)
+    self.assertEqual(1, control_message.protocol_version)
+    self.assertEqual(stem.version.Version('0.2.1.30'), control_message.tor_version)
+    self.assertEqual((AuthMethod.NONE, ), control_message.auth_methods)
+    self.assertEqual((), control_message.unknown_auth_methods)
+    self.assertEqual(None, control_message.cookie_path)
 
   def test_password_auth(self):
     """
@@ -103,7 +103,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
 
     control_message = mocking.get_message(PASSWORD_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
-    self.assertEquals((AuthMethod.PASSWORD, ), control_message.auth_methods)
+    self.assertEqual((AuthMethod.PASSWORD, ), control_message.auth_methods)
 
   def test_cookie_auth(self):
     """
@@ -113,8 +113,8 @@ class TestProtocolInfoResponse(unittest.TestCase):
 
     control_message = mocking.get_message(COOKIE_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
-    self.assertEquals((AuthMethod.COOKIE, ), control_message.auth_methods)
-    self.assertEquals('/tmp/my data\\"dir//control_auth_cookie', control_message.cookie_path)
+    self.assertEqual((AuthMethod.COOKIE, ), control_message.auth_methods)
+    self.assertEqual('/tmp/my data\\"dir//control_auth_cookie', control_message.cookie_path)
 
   def test_multiple_auth(self):
     """
@@ -123,8 +123,8 @@ class TestProtocolInfoResponse(unittest.TestCase):
 
     control_message = mocking.get_message(MULTIPLE_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
-    self.assertEquals((AuthMethod.COOKIE, AuthMethod.PASSWORD), control_message.auth_methods)
-    self.assertEquals('/home/atagar/.tor/control_auth_cookie', control_message.cookie_path)
+    self.assertEqual((AuthMethod.COOKIE, AuthMethod.PASSWORD), control_message.auth_methods)
+    self.assertEqual('/home/atagar/.tor/control_auth_cookie', control_message.cookie_path)
 
   def test_unknown_auth(self):
     """
@@ -133,8 +133,8 @@ class TestProtocolInfoResponse(unittest.TestCase):
 
     control_message = mocking.get_message(UNKNOWN_AUTH)
     stem.response.convert('PROTOCOLINFO', control_message)
-    self.assertEquals((AuthMethod.UNKNOWN, AuthMethod.PASSWORD), control_message.auth_methods)
-    self.assertEquals(('MAGIC', 'PIXIE_DUST'), control_message.unknown_auth_methods)
+    self.assertEqual((AuthMethod.UNKNOWN, AuthMethod.PASSWORD), control_message.auth_methods)
+    self.assertEqual(('MAGIC', 'PIXIE_DUST'), control_message.unknown_auth_methods)
 
   def test_minimum_response(self):
     """
@@ -145,11 +145,11 @@ class TestProtocolInfoResponse(unittest.TestCase):
     control_message = mocking.get_message(MINIMUM_RESPONSE)
     stem.response.convert('PROTOCOLINFO', control_message)
 
-    self.assertEquals(5, control_message.protocol_version)
-    self.assertEquals(None, control_message.tor_version)
-    self.assertEquals((), control_message.auth_methods)
-    self.assertEquals((), control_message.unknown_auth_methods)
-    self.assertEquals(None, control_message.cookie_path)
+    self.assertEqual(5, control_message.protocol_version)
+    self.assertEqual(None, control_message.tor_version)
+    self.assertEqual((), control_message.auth_methods)
+    self.assertEqual((), control_message.unknown_auth_methods)
+    self.assertEqual(None, control_message.cookie_path)
 
   @patch('stem.util.proc.is_available', Mock(return_value = False))
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -178,7 +178,7 @@ class TestProtocolInfoResponse(unittest.TestCase):
 
       stem.connection._expand_cookie_path(control_message, stem.util.system.pid_by_name, 'tor')
 
-      self.assertEquals(os.path.join('/tmp/foo', 'tor-browser_en-US', 'Data', 'control_auth_cookie'), control_message.cookie_path)
+      self.assertEqual(os.path.join('/tmp/foo', 'tor-browser_en-US', 'Data', 'control_auth_cookie'), control_message.cookie_path)
 
     # exercise cookie expansion where both calls fail (should work, just
     # leaving the path unexpanded)
@@ -186,4 +186,4 @@ class TestProtocolInfoResponse(unittest.TestCase):
     with patch('stem.util.system.call', Mock(return_value = None)):
       control_message = mocking.get_message(RELATIVE_COOKIE_PATH)
       stem.response.convert('PROTOCOLINFO', control_message)
-      self.assertEquals('./tor-browser_en-US/Data/control_auth_cookie', control_message.cookie_path)
+      self.assertEqual('./tor-browser_en-US/Data/control_auth_cookie', control_message.cookie_path)
diff --git a/test/unit/tutorial.py b/test/unit/tutorial.py
index 5a861b6..883c4a7 100644
--- a/test/unit/tutorial.py
+++ b/test/unit/tutorial.py
@@ -3,7 +3,6 @@ Tests for the examples given in stem's tutorial.
 """
 
 import io
-import StringIO
 import unittest
 
 from stem.control import Controller
@@ -14,8 +13,10 @@ from test import mocking
 try:
   # added in python 3.3
   from unittest.mock import Mock, patch
+  from io import StringIO
 except ImportError:
   from mock import Mock, patch
+  from StringIO import StringIO
 
 OVER_THE_RIVER_OUTPUT = """\
  * Connecting to tor
@@ -32,7 +33,7 @@ MIRROR_MIRROR_OUTPUT = """\
 
 
 class TestTutorial(unittest.TestCase):
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.control.Controller.from_port', spec = Controller)
   def test_the_little_relay_that_could(self, from_port_mock, stdout_mock):
     def tutorial_example():
@@ -44,7 +45,7 @@ class TestTutorial(unittest.TestCase):
         bytes_read = controller.get_info('traffic/read')
         bytes_written = controller.get_info('traffic/written')
 
-        print 'My Tor relay has read %s bytes and written %s.' % (bytes_read, bytes_written)
+        print('My Tor relay has read %s bytes and written %s.' % (bytes_read, bytes_written))
 
     controller = from_port_mock().__enter__()
     controller.get_info.side_effect = lambda arg: {
@@ -55,7 +56,7 @@ class TestTutorial(unittest.TestCase):
     tutorial_example()
     self.assertEqual('My Tor relay has read 33406 bytes and written 29649.\n', stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('shutil.rmtree')
   @patch('stem.control.Controller.from_port', spec = Controller)
   def test_over_the_river(self, from_port_mock, rmtree_mock, stdout_mock):
@@ -69,7 +70,7 @@ class TestTutorial(unittest.TestCase):
       def index():
         return "<h1>Hi Grandma!</h1>"
 
-      print ' * Connecting to tor'
+      print(' * Connecting to tor')
 
       with Controller.from_port() as controller:
         controller.authenticate()
@@ -82,16 +83,16 @@ class TestTutorial(unittest.TestCase):
         # Create a hidden service where visitors of port 80 get redirected to local
         # port 5000 (this is where flask runs by default).
 
-        print " * Creating our hidden service in %s" % hidden_service_dir
+        print(" * Creating our hidden service in %s" % hidden_service_dir)
         result = controller.create_hidden_service(hidden_service_dir, 80, target_port = 5000)
 
         # The hostname is only available we can read the hidden service directory.
         # This requires us to be running with the same user as tor.
 
         if result.hostname:
-          print " * Our service is available at %s, press ctrl+c to quit" % result.hostname
+          print(" * Our service is available at %s, press ctrl+c to quit" % result.hostname)
         else:
-          print " * Unable to determine our service's hostname, probably due to being unable to read the hidden service directory"
+          print(" * Unable to determine our service's hostname, probably due to being unable to read the hidden service directory")
 
         try:
           app.run()
@@ -100,7 +101,7 @@ class TestTutorial(unittest.TestCase):
           # want to delete the hidden service directory if you'd like to have this
           # same *.onion address in the future.
 
-          print " * Shutting down our hidden service"
+          print(" * Shutting down our hidden service")
           controller.remove_hidden_service(hidden_service_dir)
           shutil.rmtree(hidden_service_dir)
 
@@ -121,7 +122,7 @@ class TestTutorial(unittest.TestCase):
 
     self.assertEqual(OVER_THE_RIVER_OUTPUT, stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.descriptor.remote.DescriptorDownloader')
   def test_mirror_mirror_on_the_wall_1(self, downloader_mock, stdout_mock):
     def tutorial_example():
@@ -131,16 +132,16 @@ class TestTutorial(unittest.TestCase):
 
       try:
         for desc in downloader.get_consensus().run():
-          print 'found relay %s (%s)' % (desc.nickname, desc.fingerprint)
+          print('found relay %s (%s)' % (desc.nickname, desc.fingerprint))
       except Exception as exc:
-        print 'Unable to retrieve the consensus: %s' % exc
+        print('Unable to retrieve the consensus: %s' % exc)
 
     downloader_mock().get_consensus().run.return_value = [mocking.get_router_status_entry_v2()]
 
     tutorial_example()
     self.assertEqual('found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n', stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.control.Controller.from_port', spec = Controller)
   def test_mirror_mirror_on_the_wall_2(self, from_port_mock, stdout_mock):
     def tutorial_example():
@@ -150,7 +151,7 @@ class TestTutorial(unittest.TestCase):
         controller.authenticate()
 
         for desc in controller.get_network_statuses():
-          print 'found relay %s (%s)' % (desc.nickname, desc.fingerprint)
+          print('found relay %s (%s)' % (desc.nickname, desc.fingerprint))
 
     controller = from_port_mock().__enter__()
     controller.get_network_statuses.return_value = [mocking.get_router_status_entry_v2()]
@@ -158,14 +159,14 @@ class TestTutorial(unittest.TestCase):
     tutorial_example()
     self.assertEqual('found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n', stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('%s.open' % __name__, create = True)
   def test_mirror_mirror_on_the_wall_3(self, open_mock, stdout_mock):
     def tutorial_example():
       from stem.descriptor import parse_file
 
       for desc in parse_file(open('/home/atagar/.tor/cached-consensus')):
-        print 'found relay %s (%s)' % (desc.nickname, desc.fingerprint)
+        print('found relay %s (%s)' % (desc.nickname, desc.fingerprint))
 
     test_file = io.BytesIO(mocking.get_network_status_document_v3(
       routers = [mocking.get_router_status_entry_v3()],
@@ -178,7 +179,7 @@ class TestTutorial(unittest.TestCase):
     tutorial_example()
     self.assertEqual('found relay caerSidi (A7569A83B5706AB1B1A9CB52EFF7D2D32E4553EB)\n', stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.descriptor.reader.DescriptorReader', spec = DescriptorReader)
   def test_mirror_mirror_on_the_wall_4(self, reader_mock, stdout_mock):
     def tutorial_example():
@@ -186,7 +187,7 @@ class TestTutorial(unittest.TestCase):
 
       with DescriptorReader(['/home/atagar/server-descriptors-2013-03.tar']) as reader:
         for desc in reader:
-          print 'found relay %s (%s)' % (desc.nickname, desc.fingerprint)
+          print('found relay %s (%s)' % (desc.nickname, desc.fingerprint))
 
     reader = reader_mock().__enter__()
     reader.__iter__.return_value = iter([mocking.get_relay_server_descriptor()])
@@ -194,7 +195,7 @@ class TestTutorial(unittest.TestCase):
     tutorial_example()
     self.assertEqual('found relay caerSidi (None)\n', stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.descriptor.remote.DescriptorDownloader')
   @patch('stem.descriptor.server_descriptor.RelayDescriptor._verify_digest', Mock())
   def test_mirror_mirror_on_the_wall_5(self, downloader_mock, stdout_mock):
@@ -213,7 +214,7 @@ class TestTutorial(unittest.TestCase):
             if desc.exit_policy.is_exiting_allowed():
               bw_to_relay.setdefault(desc.observed_bandwidth, []).append(desc.nickname)
         except Exception as exc:
-          print 'Unable to retrieve the server descriptors: %s' % exc
+          print('Unable to retrieve the server descriptors: %s' % exc)
 
         return bw_to_relay
 
@@ -222,9 +223,9 @@ class TestTutorial(unittest.TestCase):
       bw_to_relay = get_bw_to_relay()
       count = 1
 
-      for bw_value in sorted(bw_to_relay.keys(), reverse = True):
+      for bw_value in sorted(list(bw_to_relay.keys()), reverse = True):
         for nickname in bw_to_relay[bw_value]:
-          print '%i. %s (%s/s)' % (count, nickname, str_tools.size_label(bw_value, 2))
+          print('%i. %s (%s/s)' % (count, nickname, str_tools.size_label(bw_value, 2)))
           count += 1
 
           if count > 15:
diff --git a/test/unit/tutorial_examples.py b/test/unit/tutorial_examples.py
index 0a64bff..b277192 100644
--- a/test/unit/tutorial_examples.py
+++ b/test/unit/tutorial_examples.py
@@ -4,9 +4,13 @@ Tests for the examples given in stem's tutorial.
 
 import collections
 import itertools
-import StringIO
 import unittest
 
+try:
+  from StringIO import StringIO
+except ImportError:
+  from io import StringIO
+
 import stem.response
 import stem.descriptor.remote
 
@@ -25,6 +29,8 @@ try:
   from unittest.mock import Mock, patch
 except ImportError:
   from mock import Mock, patch
+import sys
+oldstdout = sys.stdout
 
 CIRC_CONTENT = '650 CIRC %d %s \
 %s \
@@ -32,7 +38,7 @@ PURPOSE=%s'
 
 PATH_CONTENT = '$%s=%s,$%s=%s,$%s=%s'
 
-LIST_CIRCUITS_OUTPUT = """\
+LIST_CIRCUITS_OUTPUT = u"""\
 
 Circuit 4 (GENERAL)
  |- B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C (ByTORAndTheSnowDog, 173.209.180.61)
@@ -50,7 +56,7 @@ Circuit 10 (GENERAL)
  +- 65242C91BFF30F165DA4D132C81A9EBA94B71D62 (torexit16, 176.67.169.171)
 """
 
-EXIT_USED_OUTPUT = """\
+EXIT_USED_OUTPUT = u"""\
 Tracking requests for tor exits. Press 'enter' to end.
 
 Exit relay for our connection to 64.15.112.44:80
@@ -61,7 +67,7 @@ Exit relay for our connection to 64.15.112.44:80
 
 """
 
-OUTDATED_RELAYS_OUTPUT = """\
+OUTDATED_RELAYS_OUTPUT = u"""\
 Checking for outdated relays...
 
   0.1.0           Sambuddha Basu
@@ -127,7 +133,7 @@ def _get_router_status(address = None, port = None, nickname = None, fingerprint
 
 
 class TestTutorialExamples(unittest.TestCase):
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.control.Controller.from_port', spec = Controller)
   def test_list_circuits(self, from_port_mock, stdout_mock):
     def tutorial_example():
@@ -141,8 +147,7 @@ class TestTutorialExamples(unittest.TestCase):
           if circ.status != CircStatus.BUILT:
             continue
 
-          print
-          print "Circuit %s (%s)" % (circ.id, circ.purpose)
+          print("\nCircuit %s (%s)" % (circ.id, circ.purpose))
 
           for i, entry in enumerate(circ.path):
             div = '+' if (i == len(circ.path) - 1) else '|'
@@ -151,7 +156,7 @@ class TestTutorialExamples(unittest.TestCase):
             desc = controller.get_network_status(fingerprint, None)
             address = desc.address if desc else 'unknown'
 
-            print " %s- %s (%s, %s)" % (div, fingerprint, nickname, address)
+            print(" %s- %s (%s, %s)" % (div, fingerprint, nickname, address))
 
     path_1 = ('B1FA7D51B8B6F0CB585D944F450E7C06EDE7E44C', 'ByTORAndTheSnowDog')
     path_2 = ('0DD9935C5E939CFA1E07B8DDA6D91C1A2A9D9338', 'afo02')
@@ -181,7 +186,7 @@ class TestTutorialExamples(unittest.TestCase):
     tutorial_example()
     self.assertEqual(LIST_CIRCUITS_OUTPUT, stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.control.Controller.from_port', spec = Controller)
   def test_exit_used(self, from_port_mock, stdout_mock):
     def tutorial_example(mock_event):
@@ -191,8 +196,7 @@ class TestTutorialExamples(unittest.TestCase):
       from stem.control import EventType, Controller
 
       def main():
-        print "Tracking requests for tor exits. Press 'enter' to end."
-        print
+        print("Tracking requests for tor exits. Press 'enter' to end.\n")
 
         with Controller.from_port() as controller:
           controller.authenticate()
@@ -209,12 +213,11 @@ class TestTutorialExamples(unittest.TestCase):
           exit_fingerprint = circ.path[-1][0]
           exit_relay = controller.get_network_status(exit_fingerprint)
 
-          print "Exit relay for our connection to %s" % (event.target)
-          print "  address: %s:%i" % (exit_relay.address, exit_relay.or_port)
-          print "  fingerprint: %s" % exit_relay.fingerprint
-          print "  nickname: %s" % exit_relay.nickname
-          print "  locale: %s" % controller.get_info("ip-to-country/%s" % exit_relay.address, 'unknown')
-          print
+          print("Exit relay for our connection to %s" % (event.target))
+          print("  address: %s:%i" % (exit_relay.address, exit_relay.or_port))
+          print("  fingerprint: %s" % exit_relay.fingerprint)
+          print("  nickname: %s" % exit_relay.nickname)
+          print("  locale: %s\n" % controller.get_info("ip-to-country/%s" % exit_relay.address, 'unknown'))
 
       main()
 
@@ -232,10 +235,9 @@ class TestTutorialExamples(unittest.TestCase):
     controller.get_info.return_value = 'unknown'
 
     tutorial_example(event)
-
     self.assertEqual(EXIT_USED_OUTPUT, stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.descriptor.remote.DescriptorDownloader')
   def test_outdated_relays(self, downloader_mock, stdout_mock):
     def tutorial_example():
@@ -245,19 +247,17 @@ class TestTutorialExamples(unittest.TestCase):
       downloader = DescriptorDownloader()
       count, with_contact = 0, 0
 
-      print "Checking for outdated relays..."
-      print
+      print("Checking for outdated relays...\n")
 
       for desc in downloader.get_server_descriptors():
         if desc.tor_version < Version('0.2.3.0'):
           count += 1
 
           if desc.contact:
-            print '  %-15s %s' % (desc.tor_version, desc.contact.decode("utf-8", "replace"))
+            print('  %-15s %s' % (desc.tor_version, desc.contact.decode("utf-8", "replace")))
             with_contact += 1
 
-      print
-      print "%i outdated relays found, %i had contact information" % (count, with_contact)
+      print("\n%i outdated relays found, %i had contact information" % (count, with_contact))
 
     downloader_mock().get_server_descriptors.return_value = [
       get_relay_server_descriptor({'platform': 'node-Tor 0.2.3.0 on Linux x86_64'}),
@@ -270,7 +270,7 @@ class TestTutorialExamples(unittest.TestCase):
 
     self.assertEqual(OUTDATED_RELAYS_OUTPUT, stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.descriptor.remote.Query')
   @patch('stem.descriptor.remote.get_authorities')
   def test_compare_flags(self, get_authorities_mock, query_mock, stdout_mock):
@@ -282,7 +282,7 @@ class TestTutorialExamples(unittest.TestCase):
       downloader = remote.DescriptorDownloader(document_handler = DocumentHandler.DOCUMENT)
       queries = collections.OrderedDict()  # needed so output's order matches what's expected
 
-      for name, authority in remote.get_authorities().items():
+      for name, authority in list(remote.get_authorities().items()):
         if authority.v3ident is None:
           continue  # authority doens't vote if it lacks a v3ident
 
@@ -291,14 +291,14 @@ class TestTutorialExamples(unittest.TestCase):
       # Wait for the votes to finish being downloaded, this produces a dictionary of
       # authority nicknames to their vote.
 
-      votes = dict((name, query.run()[0]) for (name, query) in queries.items())
+      votes = dict((name, query.run()[0]) for (name, query) in list(queries.items()))
 
       # Get a superset of all the fingerprints in all the votes.
 
       all_fingerprints = set()
 
-      for vote in votes.values():
-        all_fingerprints.update(vote.routers.keys())
+      for vote in list(votes.values()):
+        all_fingerprints.update(list(vote.routers.keys()))
 
       # Finally, compare moria1's votes to maatuska.
 
@@ -307,15 +307,15 @@ class TestTutorialExamples(unittest.TestCase):
         maatuska_vote = votes['maatuska'].routers.get(fingerprint)
 
         if not moria1_vote and not maatuska_vote:
-          print "both moria1 and maatuska haven't voted about %s" % fingerprint
+          print("both moria1 and maatuska haven't voted about %s" % fingerprint)
         elif not moria1_vote:
-          print "moria1 hasn't voted about %s" % fingerprint
+          print("moria1 hasn't voted about %s" % fingerprint)
         elif not maatuska_vote:
-          print "maatuska hasn't voted about %s" % fingerprint
+          print("maatuska hasn't voted about %s" % fingerprint)
         elif 'Running' in moria1_vote.flags and 'Running' not in maatuska_vote.flags:
-          print "moria1 has the Running flag but maatuska doesn't: %s" % fingerprint
+          print("moria1 has the Running flag but maatuska doesn't: %s" % fingerprint)
         elif 'Running' in maatuska_vote.flags and 'Running' not in moria1_vote.flags:
-          print "maatuska has the Running flag but moria1 doesn't: %s" % fingerprint
+          print("maatuska has the Running flag but moria1 doesn't: %s" % fingerprint)
 
     get_authorities_mock().items.return_value = [('moria1', DIRECTORY_AUTHORITIES['moria1']), ('maatuska', DIRECTORY_AUTHORITIES['maatuska'])]
 
@@ -353,7 +353,7 @@ class TestTutorialExamples(unittest.TestCase):
     tutorial_example()
     self.assertEqual(COMPARE_FLAGS_OUTPUT, stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.descriptor.remote.get_authorities')
   @patch('stem.descriptor.remote.DescriptorDownloader.query')
   def test_votes_by_bandwidth_authorities(self, query_mock, get_authorities_mock, stdout_mock):
@@ -365,16 +365,16 @@ class TestTutorialExamples(unittest.TestCase):
       queries = {}
       downloader = remote.DescriptorDownloader()
 
-      for authority in remote.get_authorities().values():
+      for authority in list(remote.get_authorities().values()):
         if authority.is_bandwidth_authority:
           queries[authority.nickname] = downloader.query(
             '/tor/status-vote/current/authority',
             endpoints = [(authority.address, authority.dir_port)],
           )
 
-      for authority_name, query in queries.items():
+      for authority_name, query in list(queries.items()):
         try:
-          print "Getting %s's vote from %s:" % (authority_name, query.download_url)
+          print("Getting %s's vote from %s:" % (authority_name, query.download_url))
 
           measured, unmeasured = 0, 0
 
@@ -384,9 +384,9 @@ class TestTutorialExamples(unittest.TestCase):
             else:
               unmeasured += 1
 
-          print '  %i measured entries and %i unmeasured' % (measured, unmeasured)
+          print('  %i measured entries and %i unmeasured' % (measured, unmeasured))
         except Exception as exc:
-          print "  failed to get the vote (%s)" % exc
+          print("  failed to get the vote (%s)" % exc)
 
     directory_values = [
       DIRECTORY_AUTHORITIES['gabelmoo'],
@@ -422,7 +422,7 @@ class TestTutorialExamples(unittest.TestCase):
     tutorial_example()
     self.assertEqual(VOTES_BY_BANDWIDTH_AUTHORITIES_OUTPUT, stdout_mock.getvalue())
 
-  @patch('sys.stdout', new_callable = StringIO.StringIO)
+  @patch('sys.stdout', new_callable = StringIO)
   @patch('stem.descriptor.parse_file')
   @patch('%s.open' % __name__, create = True)
   @patch('stem.descriptor.remote.Query')
@@ -446,8 +446,8 @@ class TestTutorialExamples(unittest.TestCase):
         document_handler = DocumentHandler.DOCUMENT,
       ))
 
-      for fingerprint, relay in consensus.routers.items():
-        print "%s: %s" % (fingerprint, relay.nickname)
+      for fingerprint, relay in list(consensus.routers.items()):
+        print("%s: %s" % (fingerprint, relay.nickname))
 
     network_status = get_network_status_document_v3(routers = (get_router_status_entry_v3(),))
     query_mock().run.return_value = [network_status]
diff --git a/test/unit/util/conf.py b/test/unit/util/conf.py
index 9519c8d..182e622 100644
--- a/test/unit/util/conf.py
+++ b/test/unit/util/conf.py
@@ -34,25 +34,25 @@ class TestConf(unittest.TestCase):
     # checks that sync causes existing contents to be applied
     test_config.set('bool_value', 'true')
     my_config = stem.util.conf.config_dict('unit_testing', my_config)
-    self.assertEquals(True, my_config['bool_value'])
+    self.assertEqual(True, my_config['bool_value'])
 
     # check a basic synchronize
     test_config.set('str_value', 'me')
-    self.assertEquals('me', my_config['str_value'])
+    self.assertEqual('me', my_config['str_value'])
 
     # synchronize with a type mismatch, should keep the old value
     test_config.set('int_value', '7a')
-    self.assertEquals(5, my_config['int_value'])
+    self.assertEqual(5, my_config['int_value'])
 
     # changes for a collection
     test_config.set('list_value', 'a', False)
-    self.assertEquals(['a'], my_config['list_value'])
+    self.assertEqual(['a'], my_config['list_value'])
 
     test_config.set('list_value', 'b', False)
-    self.assertEquals(['a', 'b'], my_config['list_value'])
+    self.assertEqual(['a', 'b'], my_config['list_value'])
 
     test_config.set('list_value', 'c', False)
-    self.assertEquals(['a', 'b', 'c'], my_config['list_value'])
+    self.assertEqual(['a', 'b', 'c'], my_config['list_value'])
 
   def test_parse_enum(self):
     """
@@ -129,18 +129,18 @@ class TestConf(unittest.TestCase):
     """
 
     test_config = stem.util.conf.get_config('unit_testing')
-    self.assertEquals([], test_config.keys())
+    self.assertEqual([], list(test_config.keys()))
 
     # tests clearing when we're already empty
     test_config.clear()
-    self.assertEquals([], test_config.keys())
+    self.assertEqual([], list(test_config.keys()))
 
     # tests clearing when we have contents
     test_config.set('hello', 'world')
-    self.assertEquals(['hello'], test_config.keys())
+    self.assertEqual(['hello'], list(test_config.keys()))
 
     test_config.clear()
-    self.assertEquals([], test_config.keys())
+    self.assertEqual([], list(test_config.keys()))
 
   def test_listeners(self):
     """
@@ -150,20 +150,20 @@ class TestConf(unittest.TestCase):
     listener_received_keys = []
 
     def test_listener(config, key):
-      self.assertEquals(config, stem.util.conf.get_config('unit_testing'))
+      self.assertEqual(config, stem.util.conf.get_config('unit_testing'))
       listener_received_keys.append(key)
 
     test_config = stem.util.conf.get_config('unit_testing')
     test_config.add_listener(test_listener)
 
-    self.assertEquals([], listener_received_keys)
+    self.assertEqual([], listener_received_keys)
     test_config.set('hello', 'world')
-    self.assertEquals(['hello'], listener_received_keys)
+    self.assertEqual(['hello'], listener_received_keys)
 
     test_config.clear_listeners()
 
     test_config.set('foo', 'bar')
-    self.assertEquals(['hello'], listener_received_keys)
+    self.assertEqual(['hello'], listener_received_keys)
 
   def test_unused_keys(self):
     """
@@ -178,10 +178,10 @@ class TestConf(unittest.TestCase):
     test_config.get('hello')
     test_config.get_value('foo')
 
-    self.assertEquals(set(['pw']), test_config.unused_keys())
+    self.assertEqual(set(['pw']), test_config.unused_keys())
 
     test_config.get('pw')
-    self.assertEquals(set(), test_config.unused_keys())
+    self.assertEqual(set(), test_config.unused_keys())
 
   def test_get(self):
     """
@@ -200,27 +200,27 @@ class TestConf(unittest.TestCase):
 
     # check that we get the default for type mismatch or missing values
 
-    self.assertEquals(5, test_config.get('foo', 5))
-    self.assertEquals(5, test_config.get('bool_value', 5))
+    self.assertEqual(5, test_config.get('foo', 5))
+    self.assertEqual(5, test_config.get('bool_value', 5))
 
     # checks that we get a string when no default is supplied
 
-    self.assertEquals('11', test_config.get('int_value'))
+    self.assertEqual('11', test_config.get('int_value'))
 
     # exercise type casting for each of the supported types
 
-    self.assertEquals(True, test_config.get('bool_value', False))
-    self.assertEquals(11, test_config.get('int_value', 0))
-    self.assertEquals(11.1, test_config.get('float_value', 0.0))
-    self.assertEquals('world', test_config.get('str_value', ''))
-    self.assertEquals(['a', 'b', 'c'], test_config.get('list_value', []))
-    self.assertEquals(('a', 'b', 'c'), test_config.get('list_value', ()))
-    self.assertEquals({'foo': 'bar'}, test_config.get('map_value', {}))
+    self.assertEqual(True, test_config.get('bool_value', False))
+    self.assertEqual(11, test_config.get('int_value', 0))
+    self.assertEqual(11.1, test_config.get('float_value', 0.0))
+    self.assertEqual('world', test_config.get('str_value', ''))
+    self.assertEqual(['a', 'b', 'c'], test_config.get('list_value', []))
+    self.assertEqual(('a', 'b', 'c'), test_config.get('list_value', ()))
+    self.assertEqual({'foo': 'bar'}, test_config.get('map_value', {}))
 
     # the get_value is similar, though only provides back a string or list
 
-    self.assertEquals('c', test_config.get_value('list_value'))
-    self.assertEquals(['a', 'b', 'c'], test_config.get_value('list_value', multiple = True))
+    self.assertEqual('c', test_config.get_value('list_value'))
+    self.assertEqual(['a', 'b', 'c'], test_config.get_value('list_value', multiple = True))
 
-    self.assertEquals(None, test_config.get_value('foo'))
-    self.assertEquals('hello', test_config.get_value('foo', 'hello'))
+    self.assertEqual(None, test_config.get_value('foo'))
+    self.assertEqual('hello', test_config.get_value('foo', 'hello'))
diff --git a/test/unit/util/connection.py b/test/unit/util/connection.py
index f0359eb..710122b 100644
--- a/test/unit/util/connection.py
+++ b/test/unit/util/connection.py
@@ -403,8 +403,8 @@ class TestConnection(unittest.TestCase):
       '1::1': '0001:0000:0000:0000:0000:0000:0000:0001',
     }
 
-    for test_arg, expected in test_values.items():
-      self.assertEquals(expected, stem.util.connection.expand_ipv6_address(test_arg))
+    for test_arg, expected in list(test_values.items()):
+      self.assertEqual(expected, stem.util.connection.expand_ipv6_address(test_arg))
 
     self.assertRaises(ValueError, stem.util.connection.expand_ipv6_address, '127.0.0.1')
 
@@ -413,10 +413,10 @@ class TestConnection(unittest.TestCase):
     Checks the get_mask_ipv4 function.
     """
 
-    self.assertEquals('255.255.255.255', stem.util.connection.get_mask_ipv4(32))
-    self.assertEquals('255.255.255.248', stem.util.connection.get_mask_ipv4(29))
-    self.assertEquals('255.255.254.0', stem.util.connection.get_mask_ipv4(23))
-    self.assertEquals('0.0.0.0', stem.util.connection.get_mask_ipv4(0))
+    self.assertEqual('255.255.255.255', stem.util.connection.get_mask_ipv4(32))
+    self.assertEqual('255.255.255.248', stem.util.connection.get_mask_ipv4(29))
+    self.assertEqual('255.255.254.0', stem.util.connection.get_mask_ipv4(23))
+    self.assertEqual('0.0.0.0', stem.util.connection.get_mask_ipv4(0))
 
     self.assertRaises(ValueError, stem.util.connection.get_mask_ipv4, -1)
     self.assertRaises(ValueError, stem.util.connection.get_mask_ipv4, 33)
@@ -426,9 +426,9 @@ class TestConnection(unittest.TestCase):
     Checks the get_mask_ipv6 function.
     """
 
-    self.assertEquals('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', stem.util.connection.get_mask_ipv6(128))
-    self.assertEquals('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE:0000', stem.util.connection.get_mask_ipv6(111))
-    self.assertEquals('0000:0000:0000:0000:0000:0000:0000:0000', stem.util.connection.get_mask_ipv6(0))
+    self.assertEqual('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF', stem.util.connection.get_mask_ipv6(128))
+    self.assertEqual('FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFE:0000', stem.util.connection.get_mask_ipv6(111))
+    self.assertEqual('0000:0000:0000:0000:0000:0000:0000:0000', stem.util.connection.get_mask_ipv6(0))
 
     self.assertRaises(ValueError, stem.util.connection.get_mask_ipv6, -1)
     self.assertRaises(ValueError, stem.util.connection.get_mask_ipv6, 129)
@@ -438,10 +438,10 @@ class TestConnection(unittest.TestCase):
     Checks the _get_masked_bits function.
     """
 
-    self.assertEquals(32, stem.util.connection._get_masked_bits('255.255.255.255'))
-    self.assertEquals(29, stem.util.connection._get_masked_bits('255.255.255.248'))
-    self.assertEquals(23, stem.util.connection._get_masked_bits('255.255.254.0'))
-    self.assertEquals(0, stem.util.connection._get_masked_bits('0.0.0.0'))
+    self.assertEqual(32, stem.util.connection._get_masked_bits('255.255.255.255'))
+    self.assertEqual(29, stem.util.connection._get_masked_bits('255.255.255.248'))
+    self.assertEqual(23, stem.util.connection._get_masked_bits('255.255.254.0'))
+    self.assertEqual(0, stem.util.connection._get_masked_bits('0.0.0.0'))
 
     self.assertRaises(ValueError, stem.util.connection._get_masked_bits, 'blarg')
     self.assertRaises(ValueError, stem.util.connection._get_masked_bits, '255.255.0.255')
@@ -462,8 +462,8 @@ class TestConnection(unittest.TestCase):
       '2001:db8::ff00:42:8329': '00100000000000010000110110111000000000000000000000000000000000000000000000000000111111110000000000000000010000101000001100101001',
     }
 
-    for test_arg, expected in test_values.items():
-      self.assertEquals(expected, stem.util.connection._get_address_binary(test_arg))
+    for test_arg, expected in list(test_values.items()):
+      self.assertEqual(expected, stem.util.connection._get_address_binary(test_arg))
 
     self.assertRaises(ValueError, stem.util.connection._get_address_binary, '')
     self.assertRaises(ValueError, stem.util.connection._get_address_binary, 'blarg')
diff --git a/test/unit/util/enum.py b/test/unit/util/enum.py
index 23f5b97..7be7a23 100644
--- a/test/unit/util/enum.py
+++ b/test/unit/util/enum.py
@@ -14,12 +14,12 @@ class TestEnum(unittest.TestCase):
     """
 
     insects = stem.util.enum.Enum('ANT', 'WASP', 'LADYBUG', 'FIREFLY')
-    self.assertEquals('Ant', insects.ANT)
-    self.assertEquals(('Ant', 'Wasp', 'Ladybug', 'Firefly'), tuple(insects))
+    self.assertEqual('Ant', insects.ANT)
+    self.assertEqual(('Ant', 'Wasp', 'Ladybug', 'Firefly'), tuple(insects))
 
     pets = stem.util.enum.Enum(('DOG', 'Skippy'), 'CAT', ('FISH', 'Nemo'))
-    self.assertEquals('Skippy', pets.DOG)
-    self.assertEquals('Cat', pets.CAT)
+    self.assertEqual('Skippy', pets.DOG)
+    self.assertEqual('Cat', pets.CAT)
 
   def test_uppercase_enum_example(self):
     """
@@ -28,7 +28,7 @@ class TestEnum(unittest.TestCase):
     """
 
     runlevels = stem.util.enum.UppercaseEnum('DEBUG', 'INFO', 'NOTICE', 'WARN', 'ERROR')
-    self.assertEquals('DEBUG', runlevels.DEBUG)
+    self.assertEqual('DEBUG', runlevels.DEBUG)
 
   def test_enum_methods(self):
     """
@@ -38,12 +38,12 @@ class TestEnum(unittest.TestCase):
     insects = stem.util.enum.Enum('ANT', 'WASP', 'LADYBUG', 'FIREFLY')
 
     # next method
-    self.assertEquals(insects.WASP, insects.next(insects.ANT))
-    self.assertEquals(insects.ANT, insects.next(insects.FIREFLY))
+    self.assertEqual(insects.WASP, insects.next(insects.ANT))
+    self.assertEqual(insects.ANT, insects.next(insects.FIREFLY))
 
     # previous method
-    self.assertEquals(insects.FIREFLY, insects.previous(insects.ANT))
-    self.assertEquals(insects.LADYBUG, insects.previous(insects.FIREFLY))
+    self.assertEqual(insects.FIREFLY, insects.previous(insects.ANT))
+    self.assertEqual(insects.LADYBUG, insects.previous(insects.FIREFLY))
 
     # keys method
-    self.assertEquals(['ANT', 'WASP', 'LADYBUG', 'FIREFLY'], insects.keys())
+    self.assertEqual(['ANT', 'WASP', 'LADYBUG', 'FIREFLY'], list(insects.keys()))
diff --git a/test/unit/util/proc.py b/test/unit/util/proc.py
index c8defd7..a612895 100644
--- a/test/unit/util/proc.py
+++ b/test/unit/util/proc.py
@@ -2,7 +2,6 @@
 Unit testing code for the stem.util.proc functions.
 """
 
-import StringIO
 import unittest
 
 from stem.util import proc
@@ -11,9 +10,10 @@ from test import mocking
 try:
   # added in python 3.3
   from unittest.mock import Mock, patch
+  from io import StringIO
 except ImportError:
   from mock import Mock, patch
-
+  from StringIO import StringIO
 
 class TestProc(unittest.TestCase):
   @patch('stem.util.proc._get_line')
@@ -26,7 +26,7 @@ class TestProc(unittest.TestCase):
       ('/proc/stat', 'btime', 'system start time'): 'btime 1001001',
     }[params]
 
-    self.assertEquals(1001001, proc.system_start_time())
+    self.assertEqual(1001001, proc.system_start_time())
 
   @patch('stem.util.proc._get_line')
   def test_physical_memory(self, get_line_mock):
@@ -38,7 +38,7 @@ class TestProc(unittest.TestCase):
       ('/proc/meminfo', 'MemTotal:', 'system physical memory'): 'MemTotal:       12345 kB',
     }[params]
 
-    self.assertEquals((12345 * 1024), proc.physical_memory())
+    self.assertEqual((12345 * 1024), proc.physical_memory())
 
   @patch('os.readlink')
   def test_cwd(self, readlink_mock):
@@ -50,7 +50,7 @@ class TestProc(unittest.TestCase):
       '/proc/24019/cwd': '/home/directory/TEST'
     }[param]
 
-    self.assertEquals('/home/directory/TEST', proc.cwd(24019))
+    self.assertEqual('/home/directory/TEST', proc.cwd(24019))
 
   @patch('stem.util.proc._get_line')
   def test_uid(self, get_line_mock):
@@ -65,7 +65,7 @@ class TestProc(unittest.TestCase):
         ('/proc/%s/status' % pid, 'Uid:', 'uid'): 'Uid: %s' % uid,
       }[params]
 
-      self.assertEquals(uid, proc.uid(pid))
+      self.assertEqual(uid, proc.uid(pid))
 
   @patch('stem.util.proc._get_lines')
   def test_memory_usage(self, get_lines_mock):
@@ -105,7 +105,7 @@ class TestProc(unittest.TestCase):
       (stat_path, '24062', 'process '): stat
     }[params]
 
-    self.assertEquals((), proc.stats(24062))
+    self.assertEqual((), proc.stats(24062))
 
     for stats in stat_combinations:
       # the stats variable is...
@@ -114,13 +114,13 @@ class TestProc(unittest.TestCase):
       # but we need...
       #   (arg1, arg2...), (resp1, resp2...).
 
-      args, response = zip(*stats)
+      args, response = list(zip(*stats))
 
       get_line_mock.side_effect = lambda *params: {
         (stat_path, '24062', 'process %s' % ', '.join(args)): stat
       }[params]
 
-      self.assertEquals(response, proc.stats(24062, *args))
+      self.assertEqual(response, proc.stats(24062, *args))
 
       # tests the case where pid = 0
 
@@ -141,7 +141,7 @@ class TestProc(unittest.TestCase):
         ('/proc/0/stat', '0', 'process %s' % ', '.join(args)): stat
       }[params]
 
-      self.assertEquals(response, proc.stats(0, *args))
+      self.assertEqual(response, proc.stats(0, *args))
 
   @patch('os.listdir')
   def test_file_descriptors_used(self, listdir_mock):
@@ -199,16 +199,16 @@ class TestProc(unittest.TestCase):
     udp = '\n A: BBBBBBBB:BBBB CCCCCCCC:CCCC DD EEEEEEEE:EEEEEEEE FF:FFFFFFFF GGGGGGGG 1111 H IIIIIIII'
 
     open_mock.side_effect = lambda param: {
-      '/proc/net/tcp': StringIO.StringIO(tcp),
-      '/proc/net/udp': StringIO.StringIO(udp)
+      '/proc/net/tcp': StringIO(tcp),
+      '/proc/net/udp': StringIO(udp)
     }[param]
 
     # tests the edge case of pid = 0
-    self.assertEquals([], proc.connections(0))
+    self.assertEqual([], proc.connections(0))
 
     expected_results = [
       ('17.17.17.17', 4369, '34.34.34.34', 8738, 'tcp'),
       ('187.187.187.187', 48059, '204.204.204.204', 52428, 'udp'),
     ]
 
-    self.assertEquals(expected_results, proc.connections(pid))
+    self.assertEqual(expected_results, proc.connections(pid))
diff --git a/test/unit/util/str_tools.py b/test/unit/util/str_tools.py
index f874be6..1805d56 100644
--- a/test/unit/util/str_tools.py
+++ b/test/unit/util/str_tools.py
@@ -15,21 +15,21 @@ class TestStrTools(unittest.TestCase):
     """
 
     # test the pydoc example
-    self.assertEquals('I Like Pepperjack!', str_tools._to_camel_case('I_LIKE_PEPPERJACK!'))
+    self.assertEqual('I Like Pepperjack!', str_tools._to_camel_case('I_LIKE_PEPPERJACK!'))
 
     # check a few edge cases
-    self.assertEquals('', str_tools._to_camel_case(''))
-    self.assertEquals('Hello', str_tools._to_camel_case('hello'))
-    self.assertEquals('Hello', str_tools._to_camel_case('HELLO'))
-    self.assertEquals('Hello  World', str_tools._to_camel_case('hello__world'))
-    self.assertEquals('Hello\tworld', str_tools._to_camel_case('hello\tWORLD'))
-    self.assertEquals('Hello\t\tWorld', str_tools._to_camel_case('hello__world', '_', '\t'))
+    self.assertEqual('', str_tools._to_camel_case(''))
+    self.assertEqual('Hello', str_tools._to_camel_case('hello'))
+    self.assertEqual('Hello', str_tools._to_camel_case('HELLO'))
+    self.assertEqual('Hello  World', str_tools._to_camel_case('hello__world'))
+    self.assertEqual('Hello\tworld', str_tools._to_camel_case('hello\tWORLD'))
+    self.assertEqual('Hello\t\tWorld', str_tools._to_camel_case('hello__world', '_', '\t'))
 
   def test_crop(self):
     # test the pydoc examples
-    self.assertEquals('This is a looo...', str_tools.crop('This is a looooong message', 17))
-    self.assertEquals('This is a...', str_tools.crop('This is a looooong message', 12))
-    self.assertEquals('', str_tools.crop('This is a looooong message', 3))
+    self.assertEqual('This is a looo...', str_tools.crop('This is a looooong message', 17))
+    self.assertEqual('This is a...', str_tools.crop('This is a looooong message', 12))
+    self.assertEqual('', str_tools.crop('This is a looooong message', 3))
 
   def test_size_label(self):
     """
@@ -37,19 +37,19 @@ class TestStrTools(unittest.TestCase):
     """
 
     # test the pydoc examples
-    self.assertEquals('1 MB', str_tools.size_label(2000000))
-    self.assertEquals('1.02 KB', str_tools.size_label(1050, 2))
-    self.assertEquals('1.025 Kilobytes', str_tools.size_label(1050, 3, True))
+    self.assertEqual('1 MB', str_tools.size_label(2000000))
+    self.assertEqual('1.02 KB', str_tools.size_label(1050, 2))
+    self.assertEqual('1.025 Kilobytes', str_tools.size_label(1050, 3, True))
 
-    self.assertEquals('0 B', str_tools.size_label(0))
-    self.assertEquals('0 Bytes', str_tools.size_label(0, is_long = True))
-    self.assertEquals('0.00 B', str_tools.size_label(0, 2))
-    self.assertEquals('-10 B', str_tools.size_label(-10))
-    self.assertEquals('80 b', str_tools.size_label(10, is_bytes = False))
-    self.assertEquals('-1 MB', str_tools.size_label(-2000000))
+    self.assertEqual('0 B', str_tools.size_label(0))
+    self.assertEqual('0 Bytes', str_tools.size_label(0, is_long = True))
+    self.assertEqual('0.00 B', str_tools.size_label(0, 2))
+    self.assertEqual('-10 B', str_tools.size_label(-10))
+    self.assertEqual('80 b', str_tools.size_label(10, is_bytes = False))
+    self.assertEqual('-1 MB', str_tools.size_label(-2000000))
 
     # checking that we round down
-    self.assertEquals('23.43 Kb', str_tools.size_label(3000, 2, is_bytes = False))
+    self.assertEqual('23.43 Kb', str_tools.size_label(3000, 2, is_bytes = False))
 
     self.assertRaises(TypeError, str_tools.size_label, None)
     self.assertRaises(TypeError, str_tools.size_label, 'hello world')
@@ -60,14 +60,14 @@ class TestStrTools(unittest.TestCase):
     """
 
     # test the pydoc examples
-    self.assertEquals('2h', str_tools.time_label(10000))
-    self.assertEquals('1.0 minute', str_tools.time_label(61, 1, True))
-    self.assertEquals('1.01 minutes', str_tools.time_label(61, 2, True))
+    self.assertEqual('2h', str_tools.time_label(10000))
+    self.assertEqual('1.0 minute', str_tools.time_label(61, 1, True))
+    self.assertEqual('1.01 minutes', str_tools.time_label(61, 2, True))
 
-    self.assertEquals('0s', str_tools.time_label(0))
-    self.assertEquals('0 seconds', str_tools.time_label(0, is_long = True))
-    self.assertEquals('0.00s', str_tools.time_label(0, 2))
-    self.assertEquals('-10s', str_tools.time_label(-10))
+    self.assertEqual('0s', str_tools.time_label(0))
+    self.assertEqual('0 seconds', str_tools.time_label(0, is_long = True))
+    self.assertEqual('0.00s', str_tools.time_label(0, 2))
+    self.assertEqual('-10s', str_tools.time_label(-10))
 
     self.assertRaises(TypeError, str_tools.time_label, None)
     self.assertRaises(TypeError, str_tools.time_label, 'hello world')
@@ -78,11 +78,11 @@ class TestStrTools(unittest.TestCase):
     """
 
     # test the pydoc examples
-    self.assertEquals(['6m', '40s'], str_tools.time_labels(400))
-    self.assertEquals(['1 hour', '40 seconds'], str_tools.time_labels(3640, True))
+    self.assertEqual(['6m', '40s'], str_tools.time_labels(400))
+    self.assertEqual(['1 hour', '40 seconds'], str_tools.time_labels(3640, True))
 
-    self.assertEquals([], str_tools.time_labels(0))
-    self.assertEquals(['-10s'], str_tools.time_labels(-10))
+    self.assertEqual([], str_tools.time_labels(0))
+    self.assertEqual(['-10s'], str_tools.time_labels(-10))
 
     self.assertRaises(TypeError, str_tools.time_labels, None)
     self.assertRaises(TypeError, str_tools.time_labels, 'hello world')
@@ -93,10 +93,10 @@ class TestStrTools(unittest.TestCase):
     """
 
     # test the pydoc examples
-    self.assertEquals('01:51', str_tools.short_time_label(111))
-    self.assertEquals('6-07:08:20', str_tools.short_time_label(544100))
+    self.assertEqual('01:51', str_tools.short_time_label(111))
+    self.assertEqual('6-07:08:20', str_tools.short_time_label(544100))
 
-    self.assertEquals('00:00', str_tools.short_time_label(0))
+    self.assertEqual('00:00', str_tools.short_time_label(0))
 
     self.assertRaises(TypeError, str_tools.short_time_label, None)
     self.assertRaises(TypeError, str_tools.short_time_label, 'hello world')
@@ -108,17 +108,17 @@ class TestStrTools(unittest.TestCase):
     """
 
     # test the pydoc examples
-    self.assertEquals(111, str_tools.parse_short_time_label('01:51'))
-    self.assertEquals(544100, str_tools.parse_short_time_label('6-07:08:20'))
+    self.assertEqual(111, str_tools.parse_short_time_label('01:51'))
+    self.assertEqual(544100, str_tools.parse_short_time_label('6-07:08:20'))
 
-    self.assertEquals(110, str_tools.parse_short_time_label('01:50.62'))
-    self.assertEquals(0, str_tools.parse_short_time_label('00:00'))
+    self.assertEqual(110, str_tools.parse_short_time_label('01:50.62'))
+    self.assertEqual(0, str_tools.parse_short_time_label('00:00'))
 
     # these aren't technically valid, but might as well allow unnecessary
     # digits to be dropped
 
-    self.assertEquals(300, str_tools.parse_short_time_label('05:0'))
-    self.assertEquals(300, str_tools.parse_short_time_label('5:00'))
+    self.assertEqual(300, str_tools.parse_short_time_label('05:0'))
+    self.assertEqual(300, str_tools.parse_short_time_label('5:00'))
 
     self.assertRaises(TypeError, str_tools.parse_short_time_label, None)
     self.assertRaises(TypeError, str_tools.parse_short_time_label, 100)
@@ -143,7 +143,7 @@ class TestStrTools(unittest.TestCase):
         datetime.datetime(2012, 11, 8, 16, 48, 41, 0),
     }
 
-    for arg, expected in test_inputs.items():
+    for arg, expected in list(test_inputs.items()):
       self.assertEqual(expected, str_tools._parse_iso_timestamp(arg))
 
     invalid_input = [
diff --git a/test/unit/util/system.py b/test/unit/util/system.py
index 911f151..d2ae43f 100644
--- a/test/unit/util/system.py
+++ b/test/unit/util/system.py
@@ -133,7 +133,7 @@ class TestSystem(unittest.TestCase):
     call_mock.return_value = None
     call_mock.side_effect = None
     self.assertFalse(system.is_running('irssi'))
-    self.assertEquals(None, system.is_running('irssi'))
+    self.assertEqual(None, system.is_running('irssi'))
 
   @patch('stem.util.system.call')
   @patch('stem.util.proc.is_available', Mock(return_value = False))
@@ -156,7 +156,7 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = 'vim' if test_input == 'success' else None
-      self.assertEquals(expected_response, system.name_by_pid(test_input))
+      self.assertEqual(expected_response, system.name_by_pid(test_input))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -172,9 +172,9 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = 1111 if test_input == 'success' else None
-      self.assertEquals(expected_response, system.pid_by_name(test_input))
+      self.assertEqual(expected_response, system.pid_by_name(test_input))
 
-    self.assertEquals([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
+    self.assertEqual([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -190,9 +190,9 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = 1111 if test_input == 'success' else None
-      self.assertEquals(expected_response, system.pid_by_name(test_input))
+      self.assertEqual(expected_response, system.pid_by_name(test_input))
 
-    self.assertEquals([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
+    self.assertEqual([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_bsd', Mock(return_value = False))
@@ -209,9 +209,9 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = 1111 if test_input == 'success' else None
-      self.assertEquals(expected_response, system.pid_by_name(test_input))
+      self.assertEqual(expected_response, system.pid_by_name(test_input))
 
-    self.assertEquals([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
+    self.assertEqual([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_bsd', Mock(return_value = True))
@@ -222,13 +222,13 @@ class TestSystem(unittest.TestCase):
     """
 
     call_mock.side_effect = mock_call(system.GET_PID_BY_NAME_PS_BSD, GET_PID_BY_NAME_PS_BSD)
-    self.assertEquals(1, system.pid_by_name('launchd'))
-    self.assertEquals(11, system.pid_by_name('DirectoryService'))
-    self.assertEquals(None, system.pid_by_name('blarg'))
+    self.assertEqual(1, system.pid_by_name('launchd'))
+    self.assertEqual(11, system.pid_by_name('DirectoryService'))
+    self.assertEqual(None, system.pid_by_name('blarg'))
 
     call_mock.side_effect = mock_call(system.GET_PID_BY_NAME_PS_BSD, GET_PID_BY_NAME_PS_BSD_MULTIPLE)
 
-    self.assertEquals([1, 41], system.pid_by_name('launchd', multiple = True))
+    self.assertEqual([1, 41], system.pid_by_name('launchd', multiple = True))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -244,9 +244,9 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = 1111 if test_input == 'success' else None
-      self.assertEquals(expected_response, system.pid_by_name(test_input))
+      self.assertEqual(expected_response, system.pid_by_name(test_input))
 
-    self.assertEquals([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
+    self.assertEqual([123, 456, 789], system.pid_by_name('multiple_results', multiple = True))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -256,10 +256,10 @@ class TestSystem(unittest.TestCase):
     """
 
     call_mock.side_effect = mock_call(system.GET_PID_BY_PORT_NETSTAT, GET_PID_BY_PORT_NETSTAT_RESULTS)
-    self.assertEquals(1641, system.pid_by_port(9051))
-    self.assertEquals(1641, system.pid_by_port('9051'))
-    self.assertEquals(None, system.pid_by_port(631))
-    self.assertEquals(None, system.pid_by_port(123))
+    self.assertEqual(1641, system.pid_by_port(9051))
+    self.assertEqual(1641, system.pid_by_port('9051'))
+    self.assertEqual(None, system.pid_by_port(631))
+    self.assertEqual(None, system.pid_by_port(123))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -269,9 +269,9 @@ class TestSystem(unittest.TestCase):
     """
 
     call_mock.side_effect = mock_call(system.GET_PID_BY_PORT_SOCKSTAT % 9051, GET_PID_BY_PORT_SOCKSTAT_RESULTS)
-    self.assertEquals(4397, system.pid_by_port(9051))
-    self.assertEquals(4397, system.pid_by_port('9051'))
-    self.assertEquals(None, system.pid_by_port(123))
+    self.assertEqual(4397, system.pid_by_port(9051))
+    self.assertEqual(4397, system.pid_by_port('9051'))
+    self.assertEqual(None, system.pid_by_port(123))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -281,10 +281,10 @@ class TestSystem(unittest.TestCase):
     """
 
     call_mock.side_effect = mock_call(system.GET_PID_BY_PORT_LSOF, GET_PID_BY_PORT_LSOF_RESULTS)
-    self.assertEquals(1745, system.pid_by_port(9051))
-    self.assertEquals(1745, system.pid_by_port('9051'))
-    self.assertEquals(329, system.pid_by_port(80))
-    self.assertEquals(None, system.pid_by_port(123))
+    self.assertEqual(1745, system.pid_by_port(9051))
+    self.assertEqual(1745, system.pid_by_port('9051'))
+    self.assertEqual(329, system.pid_by_port(80))
+    self.assertEqual(None, system.pid_by_port(123))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -295,11 +295,11 @@ class TestSystem(unittest.TestCase):
 
     lsof_query = system.GET_PID_BY_FILE_LSOF % '/tmp/foo'
     call_mock.side_effect = mock_call(lsof_query, ['4762'])
-    self.assertEquals(4762, system.pid_by_open_file('/tmp/foo'))
+    self.assertEqual(4762, system.pid_by_open_file('/tmp/foo'))
 
     call_mock.return_value = []
     call_mock.side_effect = None
-    self.assertEquals(None, system.pid_by_open_file('/tmp/somewhere_else'))
+    self.assertEqual(None, system.pid_by_open_file('/tmp/somewhere_else'))
 
   @patch('stem.util.system.call')
   @patch('stem.util.proc.is_available', Mock(return_value = False))
@@ -320,7 +320,7 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = '/home/atagar' if test_input == '3799' else None
-      self.assertEquals(expected_response, system.cwd(test_input))
+      self.assertEqual(expected_response, system.cwd(test_input))
 
   @patch('stem.util.system.call')
   @patch('stem.util.proc.is_available', Mock(return_value = False))
@@ -340,7 +340,7 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = '/Users/atagar/tor/src/or' if test_input == '75717' else None
-      self.assertEquals(expected_response, system.cwd(test_input))
+      self.assertEqual(expected_response, system.cwd(test_input))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -362,7 +362,7 @@ class TestSystem(unittest.TestCase):
 
     for test_input in responses:
       expected_response = 1 if test_input == '1111' else 0
-      self.assertEquals(expected_response, system.bsd_jail_id(test_input))
+      self.assertEqual(expected_response, system.bsd_jail_id(test_input))
 
   @patch('stem.util.system.call')
   @patch('stem.util.system.is_available', Mock(return_value = True))
@@ -374,10 +374,10 @@ class TestSystem(unittest.TestCase):
     # check when we don't have a jail
 
     call_mock.return_value = []
-    self.assertEquals(None, system.bsd_jail_path(1))
+    self.assertEqual(None, system.bsd_jail_path(1))
 
     call_mock.side_effect = mock_call(system.GET_BSD_JAIL_PATH % '1', GET_BSD_JAIL_PATH_RESULTS)
-    self.assertEquals('/usr/jails/tor-jail', system.bsd_jail_path(1))
+    self.assertEqual('/usr/jails/tor-jail', system.bsd_jail_path(1))
 
   @patch('platform.system', Mock(return_value = 'Linux'))
   @patch('os.path.join', Mock(side_effect = posixpath.join))
@@ -388,13 +388,13 @@ class TestSystem(unittest.TestCase):
     tests).
     """
 
-    self.assertEquals('', system.expand_path(''))
-    self.assertEquals('/tmp', system.expand_path('/tmp'))
-    self.assertEquals('/tmp', system.expand_path('/tmp/'))
-    self.assertEquals('/tmp', system.expand_path('.', '/tmp'))
-    self.assertEquals('/tmp', system.expand_path('./', '/tmp'))
-    self.assertEquals('/tmp/foo', system.expand_path('foo', '/tmp'))
-    self.assertEquals('/tmp/foo', system.expand_path('./foo', '/tmp'))
+    self.assertEqual('', system.expand_path(''))
+    self.assertEqual('/tmp', system.expand_path('/tmp'))
+    self.assertEqual('/tmp', system.expand_path('/tmp/'))
+    self.assertEqual('/tmp', system.expand_path('.', '/tmp'))
+    self.assertEqual('/tmp', system.expand_path('./', '/tmp'))
+    self.assertEqual('/tmp/foo', system.expand_path('foo', '/tmp'))
+    self.assertEqual('/tmp/foo', system.expand_path('./foo', '/tmp'))
 
   @patch('platform.system', Mock(return_value = 'Windows'))
   @patch('os.path.join', Mock(side_effect = ntpath.join))
@@ -405,10 +405,10 @@ class TestSystem(unittest.TestCase):
     (that's left to integ tests).
     """
 
-    self.assertEquals('', system.expand_path(''))
-    self.assertEquals('C:\\tmp', system.expand_path('C:\\tmp'))
-    self.assertEquals('C:\\tmp', system.expand_path('C:\\tmp\\'))
-    self.assertEquals('C:\\tmp', system.expand_path('.', 'C:\\tmp'))
-    self.assertEquals('C:\\tmp', system.expand_path('.\\', 'C:\\tmp'))
-    self.assertEquals('C:\\tmp\\foo', system.expand_path('foo', 'C:\\tmp'))
-    self.assertEquals('C:\\tmp\\foo', system.expand_path('.\\foo', 'C:\\tmp'))
+    self.assertEqual('', system.expand_path(''))
+    self.assertEqual('C:\\tmp', system.expand_path('C:\\tmp'))
+    self.assertEqual('C:\\tmp', system.expand_path('C:\\tmp\\'))
+    self.assertEqual('C:\\tmp', system.expand_path('.', 'C:\\tmp'))
+    self.assertEqual('C:\\tmp', system.expand_path('.\\', 'C:\\tmp'))
+    self.assertEqual('C:\\tmp\\foo', system.expand_path('foo', 'C:\\tmp'))
+    self.assertEqual('C:\\tmp\\foo', system.expand_path('.\\foo', 'C:\\tmp'))
diff --git a/test/unit/version.py b/test/unit/version.py
index df5b57e..2d9803e 100644
--- a/test/unit/version.py
+++ b/test/unit/version.py
@@ -191,7 +191,7 @@ class TestVersion(unittest.TestCase):
     requirements = stem.version._VersionRequirements()
     requirements.in_range(Version('0.2.2.0'), Version('0.2.3.0'))
 
-    for index in xrange(0, 100):
+    for index in range(0, 100):
       self.assertTrue(Version('0.2.2.%i' % index) >= requirements)
 
   def test_requirements_multiple_rules(self):
@@ -207,7 +207,7 @@ class TestVersion(unittest.TestCase):
     self.assertTrue(Version('0.2.3.0') >= requirements)
     self.assertFalse(Version('0.2.2.0') >= requirements)
 
-    for index in xrange(0, 100):
+    for index in range(0, 100):
       self.assertFalse(Version('0.2.2.%i' % index) >= requirements)
 
   def assert_versions_match(self, version, major, minor, micro, patch, status, extra):
diff --git a/test/util.py b/test/util.py
index 714d949..314b5e7 100644
--- a/test/util.py
+++ b/test/util.py
@@ -141,7 +141,7 @@ def get_help_message():
   help_msg = CONFIG['msg.help']
 
   # gets the longest target length so we can show the entries in columns
-  target_name_length = max(map(len, Target))
+  target_name_length = max(list(map(len, Target)))
   description_format = '\n    %%-%is - %%s' % target_name_length
 
   for target in Target:
@@ -192,7 +192,7 @@ def get_torrc_entries(target):
     for opt in config_csv.split(','):
       opt = opt.strip()
 
-      if opt in test.runner.Torrc.keys():
+      if opt in list(test.runner.Torrc.keys()):
         torrc_opts.append(test.runner.Torrc[opt])
       else:
         raise ValueError("'%s' isn't a test.runner.Torrc enumeration" % opt)





More information about the tor-commits mailing list