[tor-commits] [stem/master] Document namedtuples we provide

atagar at torproject.org atagar at torproject.org
Sun Dec 6 21:57:11 UTC 2015


commit 281fc886e8976169f2c48d6e364a36fd5e7f9ab7
Author: Damian Johnson <atagar at torproject.org>
Date:   Wed Nov 25 08:56:54 2015 -0800

    Document namedtuples we provide
    
    Sphinx generates useless docs for namedtuples that simply says 'Alias for field
    number 0'. Using a tip from the following for geneating useful docs...
    
      https://stackoverflow.com/questions/13785150/how-can-i-provide-sphinx-documentation-for-a-namedtuple-with-autodoc
---
 stem/control.py                              |  109 +++++++++++++-------------
 stem/descriptor/hidden_service_descriptor.py |   32 ++++----
 stem/descriptor/networkstatus.py             |   28 +++----
 stem/manual.py                               |   17 +++-
 stem/util/connection.py                      |   30 ++++---
 stem/util/test_tools.py                      |   18 +++--
 6 files changed, 118 insertions(+), 116 deletions(-)

diff --git a/stem/control.py b/stem/control.py
index 72f0552..3b1edfa 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -368,30 +368,49 @@ SERVER_DESCRIPTORS_UNSUPPORTED = "Tor is currently not configured to retrieve \
 server descriptors. As of Tor version 0.2.3.25 it downloads microdescriptors \
 instead unless you set 'UseMicrodescriptors 0' in your torrc."
 
-AccountingStats = collections.namedtuple('AccountingStats', [
-  'retrieved',
-  'status',
-  'interval_end',
-  'time_until_reset',
-  'read_bytes',
-  'read_bytes_left',
-  'read_limit',
-  'written_bytes',
-  'write_bytes_left',
-  'write_limit',
-])
-
-UserTrafficAllowed = collections.namedtuple('UserTrafficAllowed', [
-  'inbound',
-  'outbound',
-])
-
-CreateHiddenServiceOutput = collections.namedtuple('CreateHiddenServiceOutput', [
-  'path',
-  'hostname',
-  'hostname_for_client',
-  'config',
-])
+
+class AccountingStats(collections.namedtuple('AccountingStats', ['retrieved', 'status', 'interval_end', 'time_until_reset', 'read_bytes', 'read_bytes_left', 'read_limit', 'written_bytes', 'write_bytes_left', 'write_limit'])):
+  """
+  Accounting information, determining the limits where our relay suspends
+  itself.
+
+  :var float retrieved: unix timestamp for when this was fetched
+  :var str status: hibernation status of 'awake', 'soft', or 'hard'
+  :var datetime interval_end: time when our limits reset
+  :var int time_until_reset: seconds until our limits reset
+  :var int read_bytes: number of bytes we've read relaying
+  :var int read_bytes_left: number of bytes we can read until we suspend
+  :var int read_limit: reading threshold where we suspend
+  :var int written_bytes: number of bytes we've written relaying
+  :var int write_bytes_left: number of bytes we can write until we suspend
+  :var int write_limit: writing threshold where we suspend
+  """
+
+
+class UserTrafficAllowed(collections.namedtuple('UserTrafficAllowed', ['inbound', 'outbound'])):
+  """
+  Indicates if we're likely to be servicing direct user traffic or not.
+
+  :var bool inbound: if **True** we're likely providing guard or bridge connnections
+  :var bool outbound: if **True** we're likely providng exit connections
+  """
+
+
+class CreateHiddenServiceOutput(collections.namedtuple('CreateHiddenServiceOutput', ['path', 'hostname', 'hostname_for_client', 'config'])):
+  """
+  Attributes of a hidden service we've created.
+
+  Both the **hostnames** and **hostname_for_client** attributes can only be
+  provided if we're able to read the hidden service directory. If the method
+  was called with **client_names** then we may provide the
+  **hostname_for_client**, and otherwise can provide the **hostnames**.
+
+  :var str path: hidden service directory
+  :var str hostname: content of the hostname file if available
+  :var dict hostname_for_client:mapping of client names to their onion address
+    if available
+  :var dict config: tor's new hidden service configuration
+  """
 
 
 def with_default(yields = False):
@@ -1321,25 +1340,13 @@ class Controller(BaseController):
     get_accounting_stats(default = UNDEFINED)
 
     Provides stats related to our relaying limitations if AccountingMax was set
-    in our torrc. This provides a **namedtuple** with the following
-    attributes...
-
-      * retrieved (float) - unix timestamp for when this was fetched
-      * status (str) - hibernation status of 'awake', 'soft', or 'hard'
-      * interval_end (datetime)
-      * time_until_reset (int) - seconds until our limits reset
-      * read_bytes (int)
-      * read_bytes_left (int)
-      * read_limit (int)
-      * written_bytes (int)
-      * write_bytes_left (int)
-      * write_limit (int)
+    in our torrc.
 
     .. versionadded:: 1.3.0
 
     :param object default: response if the query fails
 
-    :returns: **namedtuple** with our accounting stats
+    :returns: :class:`~stem.control.AccountingStats` with our accounting stats
 
     :raises: :class:`stem.ControllerError` if unable to determine the listeners
       and no default was provided
@@ -1521,8 +1528,9 @@ class Controller(BaseController):
 
     .. versionadded:: 1.5.0
 
-    :returns: **namedtuple** with an **inbound** and **outbound** boolean
-      attribute to indicate if we're likely to have user traffic there
+    :returns: :class:`~stem.cotroller.UserTrafficAllowed` with **inbound** and
+      **outbound** boolean attributes to indicate if we're likely servicing
+      direct user traffic
     """
 
     inbound_allowed, outbound_allowed = False, False
@@ -2439,26 +2447,14 @@ class Controller(BaseController):
   def create_hidden_service(self, path, port, target_address = None, target_port = None, auth_type = None, client_names = None):
     """
     Create a new hidden service. If the directory is already present, a
-    new port is added. This provides a **namedtuple** of the following...
-
-      * path (str) - hidden service directory
-
-      * hostname (str) - Content of the hostname file, if no **client_names**
-        are provided this is the onion address of the service. This is only
-        retrieved if we can read the hidden service directory.
-
-      * hostname_for_client (dict) - mapping of client names to their onion
-        address, this is only set if the **client_names** was provided and we
-        can read the hidden service directory
-
-      * config (dict) - tor's new hidden service configuration
+    new port is added.
 
     Our *.onion address is fetched by reading the hidden service directory.
     However, this directory is only readable by the tor user, so if unavailable
     the **hostname** will be **None**.
 
-    **As of Tor 0.2.7.1 there's two ways for creating hidden services. This is
-    no longer the recommended method.** Rather, try using
+    **As of Tor 0.2.7.1 there's two ways for creating hidden services, and this
+    method is no longer recommended.** Rather, try using
     :func:`~stem.control.Controller.create_ephemeral_hidden_service` instead.
 
     .. versionadded:: 1.3.0
@@ -2474,7 +2470,8 @@ class Controller(BaseController):
     :param str auth_type: authentication type: basic, stealth or None to disable auth
     :param list client_names: client names (1-16 characters "A-Za-z0-9+-_")
 
-    :returns: **CreateHiddenServiceOutput** if we create or update a hidden service, **None** otherwise
+    :returns: :class:`~stem.cotroller.CreateHiddenServiceOutput` if we create
+      or update a hidden service, **None** otherwise
 
     :raises: :class:`stem.ControllerError` if the call fails
     """
diff --git a/stem/descriptor/hidden_service_descriptor.py b/stem/descriptor/hidden_service_descriptor.py
index 935b432..1d9ecae 100644
--- a/stem/descriptor/hidden_service_descriptor.py
+++ b/stem/descriptor/hidden_service_descriptor.py
@@ -80,7 +80,17 @@ SINGLE_INTRODUCTION_POINT_FIELDS = [
 BASIC_AUTH = 1
 STEALTH_AUTH = 2
 
-IntroductionPoint = collections.namedtuple('IntroductionPoints', INTRODUCTION_POINTS_ATTR.keys())
+
+class IntroductionPoints(collections.namedtuple('IntroductionPoints', INTRODUCTION_POINTS_ATTR.keys())):
+  """
+  :var str identifier: hash of this introduction point's identity key
+  :var str address: address of this introduction point
+  :var int port: port where this introduction point is listening
+  :var str onion_key: public key for communicating with this introduction point
+  :var str service_key: public key for communicating with this hidden service
+  :var list intro_authentication: tuples of the form (auth_type, auth_data) for
+    establishing a connection
+  """
 
 
 class DecryptionFailure(Exception):
@@ -245,21 +255,9 @@ class HiddenServiceDescriptor(Descriptor):
   @lru_cache()
   def introduction_points(self, authentication_cookie = None):
     """
-    Provided this service's introduction points. This provides a list of
-    IntroductionPoint instances, which have the following attributes...
-
-      * **identifier** (str): hash of this introduction point's identity key
-      * **address** (str): address of this introduction point
-      * **port** (int): port where this introduction point is listening
-      * **onion_key** (str): public key for communicating with this introduction point
-      * **service_key** (str): public key for communicating with this hidden service
-      * **intro_authentication** (list): tuples of the form (auth_type, auth_data)
-        for establishing a connection
-
-    :param str authentication_cookie: cookie to decrypt the introduction-points
-      if it's encrypted
+    Provided this service's introduction points.
 
-    :returns: **list** of IntroductionPoints instances
+    :returns: **list** of :class:`~stem.descriptor.hidden_service_descriptor.IntroductionPoints`
 
     :raises:
       * **ValueError** if the our introduction-points is malformed
@@ -358,7 +356,7 @@ class HiddenServiceDescriptor(Descriptor):
   @staticmethod
   def _parse_introduction_points(content):
     """
-    Provides the parsed list of IntroductionPoint for the unencrypted content.
+    Provides the parsed list of IntroductionPoints for the unencrypted content.
     """
 
     introduction_points = []
@@ -405,6 +403,6 @@ class HiddenServiceDescriptor(Descriptor):
             auth_type, auth_data = auth_value.split(' ')[:2]
             auth_entries.append((auth_type, auth_data))
 
-      introduction_points.append(IntroductionPoint(**attr))
+      introduction_points.append(IntroductionPoints(**attr))
 
     return introduction_points
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py
index 2db94a6..ce6dd73 100644
--- a/stem/descriptor/networkstatus.py
+++ b/stem/descriptor/networkstatus.py
@@ -52,16 +52,6 @@ For more information see :func:`~stem.descriptor.__init__.DocumentHandler`...
   KeyCertificate - Certificate used to authenticate an authority
   DocumentSignature - Signature of a document by a directory authority
   DirectoryAuthority - Directory authority as defined in a v3 network status document
-
-
-.. data:: PackageVersion
-
-  Latest recommended version of a package that's available.
-
-  :var str name: name of the package
-  :var str version: latest recommended version
-  :var str url: package's url
-  :var dict digests: mapping of digest types to their value
 """
 
 import collections
@@ -91,13 +81,6 @@ from stem.descriptor.router_status_entry import (
   RouterStatusEntryMicroV3,
 )
 
-PackageVersion = collections.namedtuple('PackageVersion', [
-  'name',
-  'version',
-  'url',
-  'digests',
-])
-
 # Version 2 network status document fields, tuples of the form...
 # (keyword, is_mandatory)
 
@@ -215,6 +198,17 @@ PARAM_RANGE = {
 }
 
 
+class PackageVersion(collections.namedtuple('PackageVersion', ['name', 'version', 'url', 'digests'])):
+  """
+  Latest recommended version of a package that's available.
+
+  :var str name: name of the package
+  :var str version: latest recommended version
+  :var str url: package's url
+  :var dict digests: mapping of digest types to their value
+  """
+
+
 def _parse_file(document_file, document_type = None, validate = False, is_microdescriptor = False, document_handler = DocumentHandler.ENTRIES, **kwargs):
   """
   Parses a network status and iterates over the RouterStatusEntry in it. The
diff --git a/stem/manual.py b/stem/manual.py
index 1e2a8f1..be25353 100644
--- a/stem/manual.py
+++ b/stem/manual.py
@@ -75,8 +75,6 @@ except ImportError:
   import urllib2 as urllib
 
 Category = stem.util.enum.Enum('GENERAL', 'CLIENT', 'RELAY', 'DIRECTORY', 'AUTHORITY', 'HIDDEN_SERVICE', 'TESTING', 'UNKNOWN')
-ConfigOption = collections.namedtuple('ConfigOption', ['category', 'name', 'usage', 'summary', 'description'])
-
 GITWEB_MANUAL_URL = 'https://gitweb.torproject.org/tor.git/plain/doc/tor.1.txt'
 
 CATEGORY_SECTIONS = {
@@ -90,6 +88,19 @@ CATEGORY_SECTIONS = {
 }
 
 
+class ConfigOption(collections.namedtuple('ConfigOption', ['category', 'name', 'usage', 'summary', 'description'])):
+  """
+  Tor configuration attribute found in its torrc.
+
+  :var stem.manual.Category category: category the config option was listed
+    under, this is Category.UNKNOWN if we didn't recognize the category
+  :var str name: name of the configuration option
+  :var str usage: arguments accepted by the option
+  :var str summary: brief description of what the option does
+  :var str description: longer manual description with details
+  """
+
+
 @lru_cache()
 def _config(lowercase = True):
   """
@@ -207,7 +218,7 @@ class Manual(object):
   :var dict signals: mapping of signals tor accepts to their description
   :var dict files: mapping of file paths to their description
 
-  :var dict config_option: **ConfigOption** tuples for tor configuration options
+  :var dict config_option: :class:`~stem.manual.ConfigOption` tuples for tor configuration options
   """
 
   def __init__(self, name, synopsis, description, commandline_options, signals, files, config_options):
diff --git a/stem/util/connection.py b/stem/util/connection.py
index 88d70d5..951b020 100644
--- a/stem/util/connection.py
+++ b/stem/util/connection.py
@@ -74,14 +74,6 @@ Resolver = enum.Enum(
   ('BSD_PROCSTAT', 'procstat (bsd)')
 )
 
-Connection = collections.namedtuple('Connection', [
-  'local_address',
-  'local_port',
-  'remote_address',
-  'remote_port',
-  'protocol',
-])
-
 FULL_IPv4_MASK = '255.255.255.255'
 FULL_IPv6_MASK = 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF'
 
@@ -140,16 +132,22 @@ RESOLVER_FILTER = {
 }
 
 
+class Connection(collections.namedtuple('Connection', ['local_address', 'local_port', 'remote_address', 'remote_port', 'protocol'])):
+  """
+  Network connection information.
+
+  :var str local_address: ip address the connection originates from
+  :var int local_port: port the connection originates from
+  :var str remote_address: destionation ip address
+  :var int remote_port: destination port
+  :var str protocol: protocol of the connection ('tcp', 'udp', etc)
+  """
+
+
 def get_connections(resolver, process_pid = None, process_name = None):
   """
   Retrieves a list of the current connections for a given process. This
-  provides a list of Connection instances, which have five attributes...
-
-    * **local_address** (str)
-    * **local_port** (int)
-    * **remote_address** (str)
-    * **remote_port** (int)
-    * **protocol** (str, generally either 'tcp' or 'udp')
+  provides a list of :class:`~stem.util.connection.Connection`.
 
   .. versionadded:: 1.1.0
 
@@ -157,7 +155,7 @@ def get_connections(resolver, process_pid = None, process_name = None):
   :param int process_pid: pid of the process to retrieve
   :param str process_name: name of the process to retrieve
 
-  :returns: **list** of Connection instances
+  :returns: **list** of :class:`~stem.util.connection.Connection` instances
 
   :raises:
     * **ValueError** if using **Resolver.PROC** or **Resolver.BSD_PROCSTAT**
diff --git a/stem/util/test_tools.py b/stem/util/test_tools.py
index dac9fc7..7c97444 100644
--- a/stem/util/test_tools.py
+++ b/stem/util/test_tools.py
@@ -31,11 +31,15 @@ CONFIG = stem.util.conf.config_dict('test', {
   'exclude_paths': [],
 })
 
-Issue = collections.namedtuple('Issue', [
-  'line_number',
-  'message',
-  'line',
-])
+
+class Issue(collections.namedtuple('Issue', ['line_number', 'message', 'line'])):
+  """
+  Issue encountered by pyflakes or pep8.
+
+  :var int line_number: line number the issue occured on
+  :var str message: description of the issue
+  :var str line: content of the line the issue is about
+  """
 
 
 def clean_orphaned_pyc(paths):
@@ -168,7 +172,7 @@ def stylistic_issues(paths, check_newlines = False, check_exception_keyword = Fa
   :param bool prefer_single_quotes: standardize on using single rather than
     double quotes for strings, when reasonable
 
-  :returns: **dict** of the form ``path => [(line_number, message)...]``
+  :returns: dict of paths list of :class:`stem.util.test_tools.Issue` instances
   """
 
   issues = {}
@@ -256,7 +260,7 @@ def pyflakes_issues(paths):
 
   :param list paths: paths to search for problems
 
-  :returns: dict of the form ``path => [(line_number, message)...]``
+  :returns: dict of paths list of :class:`stem.util.test_tools.Issue` instances
   """
 
   issues = {}





More information about the tor-commits mailing list