[Pkg-privacy-commits] [pyptlib] 80/136: refactor Config API slightly, tests passed - add a unified Config.declareSupports() method for use in initialisation - remove most of the check* methods, instead do this directly in declareSupports() - move common logic from {client, server}.init() into declareSupports() - add a unified envvar validation system using composable first-class functions - exceptions thrown during validation automatic trigger config.writeEnvError()

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 13:25:12 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository pyptlib.

commit 6b6b3579a0620437582eafea197924ce8e2de50e
Author: Ximin Luo <infinity0 at gmx.com>
Date:   Wed Aug 14 14:58:47 2013 +0100

    refactor Config API slightly, tests passed
    - add a unified Config.declareSupports() method for use in initialisation
      - remove most of the check* methods, instead do this directly in declareSupports()
      - move common logic from {client,server}.init() into declareSupports()
    - add a unified envvar validation system using composable first-class functions
      - exceptions thrown during validation automatic trigger config.writeEnvError()
---
 pyptlib/client.py        |  37 ++-----------
 pyptlib/client_config.py |  16 ++----
 pyptlib/config.py        | 132 +++++++++++++++++++++--------------------------
 pyptlib/server.py        |  39 ++------------
 pyptlib/server_config.py |  73 +++++++++++++-------------
 5 files changed, 106 insertions(+), 191 deletions(-)

diff --git a/pyptlib/client.py b/pyptlib/client.py
index 59e4944..f8dc6d5 100644
--- a/pyptlib/client.py
+++ b/pyptlib/client.py
@@ -8,6 +8,7 @@ Public client-side pyptlib API.
 from pyptlib.config import EnvError
 from pyptlib.client_config import ClientConfig
 
+
 def init(supported_transports):
     """
     Bootstrap client-side managed-proxy mode.
@@ -27,21 +28,12 @@ def init(supported_transports):
 
     :raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
     """
-
-    supportedTransportVersion = '1'
-
     config = ClientConfig()
 
-    if config.checkManagedTransportVersion(supportedTransportVersion):
-        config.writeVersion(supportedTransportVersion)
-    else:
-        config.writeVersionError()
-        raise EnvError("Unsupported managed proxy protocol version (%s)" %
-                           str(config.getManagedTransportVersions()))
-
+    wanted = config.declareSupports(supported_transports)
     retval = {}
     retval['state_loc'] = config.getStateLocation()
-    retval['transports'] = _getTransportsList(supported_transports, config)
+    retval['transports'] = wanted['transports']
 
     return retval
 
@@ -85,26 +77,3 @@ def reportEnd():
 
     config = ClientConfig()
     config.writeMethodEnd()
-
-def _getTransportsList(supported_transports, config):
-    """
-    Figure out which transports the application should launch, based on
-    the transports it supports and on the transports that Tor wants it
-    to spawn.
-
-    :param list supported_transports: Transports that the application supports.
-    :param :class:`pyptlib.client_config.ClientConfig` config: Configuration of Tor.
-
-    :returns: A list of transports that the application should launch.
-    """
-    transports = []
-
-    if config.getAllTransportsEnabled():
-        return supported_transports
-
-    for transport in config.getClientTransports():
-        if transport in supported_transports:
-            transports.append(transport)
-
-    return transports
-
diff --git a/pyptlib/client_config.py b/pyptlib/client_config.py
index 33e34a6..3099600 100644
--- a/pyptlib/client_config.py
+++ b/pyptlib/client_config.py
@@ -5,6 +5,8 @@
 Low-level parts of pyptlib that are only useful to clients.
 """
 
+import sys
+
 from pyptlib.config import Config
 
 class ClientConfig(Config):
@@ -16,22 +18,11 @@ class ClientConfig(Config):
     def __init__(self):
         Config.__init__(self)
 
-        self.transports = self.get('TOR_PT_CLIENT_TRANSPORTS').split(',')
+        self.transports = self.getEnv('TOR_PT_CLIENT_TRANSPORTS').split(',')
         if '*' in self.transports:
             self.allTransportsEnabled = True
             self.transports.remove('*')
 
-    def getClientTransports(self): # XXX why is this client-specific ???
-        """
-        Return a list of strings representing the client transports reported by Tor.
-
-        If present, the wildcard transport, '*', is stripped from this list and used to set allTransportsEnabled to True.
-
-        :returns: list of transports
-        """
-
-        return self.transports
-
     def writeMethod(self, name, socksVersion, addrport, args=None, optArgs=None):
         """
         Write a message to stdout announcing that a transport was
@@ -69,4 +60,3 @@ class ClientConfig(Config):
 
         self.emit('CMETHODS DONE')
 
-
diff --git a/pyptlib/config.py b/pyptlib/config.py
index 0490d4d..af15404 100644
--- a/pyptlib/config.py
+++ b/pyptlib/config.py
@@ -7,6 +7,23 @@ Parts of pyptlib that are useful both to clients and servers.
 
 import os, sys
 
+SUPPORTED_TRANSPORT_VERSIONS = ['1']
+
+def env_has_k(k, v):
+    """
+    A validator for Config.getEnv that returns the value of the envvar if it
+    was found, or throws ValueError if it was not.
+    """
+    if v is None: raise ValueError('Missing environment variable %s' % k)
+    return v
+
+def env_id(k, v):
+    """
+    A validator for Config.getEnv that returns the value of the envvar if it
+    was found, or None if it was not.
+    """
+    return v
+
 class Config(object):
     """
     pyptlib's configuration.
@@ -19,23 +36,10 @@ class Config(object):
     :raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
     """
 
-    stateLocation = None  # TOR_PT_STATE_LOCATION
-    managedTransportVer = []  # TOR_PT_MANAGED_TRANSPORT_VER
-    transports = []  # TOR_PT_SERVER_TRANSPORTS or TOR_PT_CLIENT_TRANSPORTS
-    allTransportsEnabled = False
-
     def __init__(self):
-        self.stateLocation = self.get('TOR_PT_STATE_LOCATION')
-        self.managedTransportVer = self.get('TOR_PT_MANAGED_TRANSPORT_VER').split(',')
-
-    def checkClientMode(self):
-        """
-        Check whether Tor wants us to run as a client or as a server.
-
-        :returns: bool -- True if Tor wants us to run as a client.
-        """
-
-        return self.check('TOR_PT_CLIENT_TRANSPORTS')
+        self.stateLocation = self.getEnv('TOR_PT_STATE_LOCATION')
+        self.managedTransportVer = self.getEnv('TOR_PT_MANAGED_TRANSPORT_VER').split(',')
+        self.allTransportsEnabled = False
 
     def getStateLocation(self):
         """
@@ -51,17 +55,6 @@ class Config(object):
 
         return self.managedTransportVer
 
-    def checkManagedTransportVersion(self, version):
-        """
-        Check if Tor supports a specific managed-proxy protocol version.
-
-        :param string version: A managed-proxy protocol version.
-
-        :returns: bool -- True if version is supported.
-        """
-
-        return version in self.managedTransportVer
-
     def getAllTransportsEnabled(self):
         """
         Check if Tor wants the application to spawn all its transpotrs.
@@ -71,71 +64,66 @@ class Config(object):
 
         return self.allTransportsEnabled
 
-    def checkTransportEnabled(self, transport):
-        """
-        Check if Tor wants the application to spawn a specific transport.
-
-        :param string transport: The name of a pluggable transport.
-
-        :returns: bool -- True if Tor wants the application to spawn that transport.
-        """
-
-        return self.allTransportsEnabled or transport in self.transports
-
-    def writeEnvError(self, message):  # ENV-ERROR
-        """
-        Announce that an error occured while parsing the environment.
-
-        :param str message: Error message.
-        """
-
-        self.emit('ENV-ERROR %s' % message)
-
-    def writeVersion(self, version):  # VERSION
+    def declareSupports(self, transports):
         """
-        Announce that a specific managed-proxy protocol version is supported.
+        Declare to Tor the versions and transports that this PT supports.
 
-        :param str version: A managed-proxy protocol version.
-        """
-
-        self.emit('VERSION %s' % version)
+        :param list transports: List of transport methods this PT supports.
 
-    def writeVersionError(self):  # VERSION-ERROR
-        """
-        Announce that we could not find a supported managed-proxy
-        protocol version.
+        :returns: {"transports": wanted_transports} -- The subset of the
+            declared inputs that were actually wanted by Tor.
         """
+        versions = SUPPORTED_TRANSPORT_VERSIONS
+        if type(transports) == str:
+            transports = [transports]
 
-        self.emit('VERSION-ERROR no-version')
+        wanted_versions = [v for v in versions if v in self.managedTransportVer]
+        if not wanted_versions:
+            self.emit('VERSION-ERROR no-version')
+            raise EnvError("Unsupported managed proxy protocol version (%s)" %
+                           self.managedTransportVer)
+        else:
+            self.emit('VERSION %s' % wanted_versions[0])
 
-    def check(self, key):
-        """
-        Check the environment for a specific environment variable.
+        if self.allTransportsEnabled:
+            wanted_transports = transports.keys()
+            unwanted_transports = []
+        else:
+            # return able in priority-order determined by plugin
+            wanted_transports = [t for t in transports if t in self.transports]
+            # return unable in priority-order as requested by Tor
+            unwanted_transports = [t for t in self.transports if t not in transports]
 
-        :param str key: Environment variable key.
+        for t in unwanted_transports:
+            self.writeMethodError(t, 'unsupported transport')
 
-        :returns: bool -- True if the environment variable is set.
-        """
+        return { 'transports': wanted_transports }
 
-        return key in os.environ
+    def writeMethodError(self, transportName, message):
+        raise NotImplementedError
 
-    def get(self, key):
+    def getEnv(self, key, validate=env_has_k):
         """
         Get the value of an environment variable.
 
         :param str key: Environment variable key.
+        :param f validate: Function that takes a var and a value and returns
+            a transformed value if it is valid, or throws an exception.
+            If the environment does not define var, value is None. By default,
+            we return the value if the environment has the variable, otherwise
+            we raise a ValueError.
 
         :returns: str -- The value of the envrionment variable.
 
         :raises: :class:`pyptlib.config.EnvError` if environment
         variable could not be found.
         """
-
-        if key in os.environ:
-            return os.environ[key]
-        else:
-            message = 'Missing environment variable %s' % key
-            self.writeEnvError(message)
+        try:
+            return validate(key, os.getenv(key))
+        except Exception, e:
+            message = 'ENV-ERROR %s' % e.message
+            print message
+            sys.stdout.flush()
             raise EnvError(message)
 
     def emit(self, msg):
diff --git a/pyptlib/server.py b/pyptlib/server.py
index ee075ec..15e5df8 100644
--- a/pyptlib/server.py
+++ b/pyptlib/server.py
@@ -31,23 +31,15 @@ def init(supported_transports):
 
     :raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
     """
-
-    supportedTransportVersion = '1'
-
     config = ServerConfig()
-
-    if config.checkManagedTransportVersion(supportedTransportVersion):
-        config.writeVersion(supportedTransportVersion)
-    else:
-        config.writeVersionError()
-        raise EnvError("Unsupported managed proxy protocol version (%s)" %
-                           str(config.getManagedTransportVersions()))
-
+    wanted = config.declareSupports(supported_transports)
+    transports = dict(((k, v) for k, v in config.getServerBindAddresses().items()
+                              if k in wanted['transports']))
     retval = {}
     retval['state_loc'] = config.getStateLocation()
     retval['orport'] = config.getORPort()
     retval['ext_orport'] = config.getExtendedORPort()
-    retval['transports'] = _getTransportsDict(supported_transports, config)
+    retval['transports'] = transports
     retval['auth_cookie_file'] = config.getAuthCookieFile()
 
     return retval
@@ -90,26 +82,3 @@ def reportEnd():
 
     config = ServerConfig()
     config.writeMethodEnd()
-
-def _getTransportsDict(supported_transports, config):
-    """
-    Figure out which transports the application should launch, based on
-    the transports it supports and on the transports that Tor wants it
-    to spawn.
-
-    :param list supported_transports: Transports that the application supports.
-    :param :class:`pyptlib.client_config.ClientConfig` config: Configuration of Tor.
-
-    :returns: A dictionary of 'transport => bind address' of transports that the application should launch.
-    """
-    transports = {}
-
-    if config.getAllTransportsEnabled():
-        return config.getServerBindAddresses()
-
-    for transport in config.getServerTransports():
-        if transport in supported_transports:
-            assert(transport in config.getServerBindAddresses())
-            transports[transport] = config.getServerBindAddresses()[transport]
-
-    return transports
diff --git a/pyptlib/server_config.py b/pyptlib/server_config.py
index ba0fa63..933f856 100644
--- a/pyptlib/server_config.py
+++ b/pyptlib/server_config.py
@@ -7,6 +7,9 @@ Low-level parts of pyptlib that are only useful to servers.
 
 import pyptlib.config as config
 import pyptlib.util as util
+import sys
+
+from pyptlib.config import env_has_k, env_id
 
 class ServerConfig(config.Config):
     """
@@ -20,63 +23,59 @@ class ServerConfig(config.Config):
     :raises: :class:`pyptlib.config.EnvError` if environment was incomplete or corrupted.
     """
     def __init__(self):
-        config.Config.__init__(self)
-
         """
         TOR_PT_EXTENDED_SERVER_PORT is optional; tor uses the empty
         string as its value if it does not support the Extended
         ORPort.
         """
-        ext_orport_tmp = self.get('TOR_PT_EXTENDED_SERVER_PORT')
-        if ext_orport_tmp == '':
-            self.extendedORPort = None
-        else:
-            self.extendedORPort = self.get_addrport('TOR_PT_EXTENDED_SERVER_PORT')
+        config.Config.__init__(self)
 
-        if self.check('TOR_PT_AUTH_COOKIE_FILE'):
-            self.authCookieFile = self.get('TOR_PT_AUTH_COOKIE_FILE')
-        else:
-            self.authCookieFile = None
+        def empty_or_valid_addr(k, v):
+            v = env_has_k(k, v)
+            if v == '': return None
+            return util.parse_addr_spec(v)
+
+        self.extendedORPort = self.getEnv('TOR_PT_EXTENDED_SERVER_PORT', empty_or_valid_addr)
 
         # Check that either both Extended ORPort and the Extended
         # ORPort Authentication Cookie are present, or neither.
-        if self.extendedORPort and not self.authCookieFile:
-            err = "Extended ORPort address provided, but no cookie file."
-            self.writeEnvError(err)
-            raise config.EnvError(err)
-        elif self.authCookieFile and not self.extendedORPort:
-            err = "Extended ORPort Authentication cookie file provided, but no Extended ORPort address."
-            self.writeEnvError(err)
-            raise config.EnvError(err)
+        if self.extendedORPort:
+            def validate_authcookie(k, v):
+                if v is None: raise ValueError("Extended ORPort address provided, but no cookie file.")
+                return v
+        else:
+            def validate_authcookie(k, v):
+                if v is not None: raise ValueError("Extended ORPort Authentication cookie file provided, but no Extended ORPort address.")
+                return v
+        self.authCookieFile = self.getEnv('TOR_PT_AUTH_COOKIE_FILE', validate_authcookie)
 
         # Get ORPort.
-        self.ORPort = self.get_addrport('TOR_PT_ORPORT')
+        self.ORPort = self.getEnv('TOR_PT_ORPORT', empty_or_valid_addr)
 
         # Get bind addresses.
-        self.serverBindAddr = {}
-        bindaddrs = self.get('TOR_PT_SERVER_BINDADDR').split(',')
-        for bindaddr in bindaddrs:
-            (transport_name, addrport) = bindaddr.split('-')
-
-            try:
+        def validate_sever_bindaddr(k, bindaddrs):
+            serverBindAddr = {}
+            bindaddrs = env_has_k(k, bindaddrs).split(',')
+            for bindaddr in bindaddrs:
+                (transport_name, addrport) = bindaddr.split('-')
                 (addr, port) = util.parse_addr_spec(addrport)
-            except ValueError, err:
-                self.writeEnvError(err)
-                raise config.EnvError(err)
-
-            self.serverBindAddr[transport_name] = (addr, port)
+                serverBindAddr[transport_name] = (addr, port)
+            return serverBindAddr
+        self.serverBindAddr = self.getEnv('TOR_PT_SERVER_BINDADDR', validate_sever_bindaddr)
 
         # Get transports.
-        self.transports = self.get('TOR_PT_SERVER_TRANSPORTS').split(',')
+        def validate_transports(k, transports):
+            transports = env_has_k(k, transports).split(',')
+            t = sorted(transports)
+            b = sorted(self.serverBindAddr.keys())
+            if t != b:
+                raise ValueError("Can't match transports with bind addresses (%s, %s)" % (t, b))
+            return transports
+        self.transports = self.getEnv('TOR_PT_SERVER_TRANSPORTS', validate_transports)
         if '*' in self.transports:
             self.allTransportsEnabled = True
             self.transports.remove('*')
 
-        if sorted(self.transports) != sorted(self.serverBindAddr.keys()):
-            err = "Can't match transports with bind addresses (%s, %s)" % (self.transports, self.serverBindAddr.keys())
-            self.writeEnvError(err)
-            raise config.EnvError(err)
-
     def getExtendedORPort(self):
         """
         :returns: :attr:`pyptlib.server_config.ServerConfig.extendedORPort`

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/pyptlib.git



More information about the Pkg-privacy-commits mailing list