
commit 281fc886e8976169f2c48d6e364a36fd5e7f9ab7 Author: Damian Johnson <atagar@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-docume... --- 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 = {}