commit bd978039dcb35b32793ff1561bc035566fc4e891
Author: David Fifield <david(a)bamsoftware.com>
Date: Sat Nov 8 16:11:55 2014 -0800
Typo.
---
wsgi/reflect.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/wsgi/reflect.py b/wsgi/reflect.py
index 8098a8f..3836254 100644
--- a/wsgi/reflect.py
+++ b/wsgi/reflect.py
@@ -12,8 +12,8 @@ REFLECTED_HEADER_FIELDS = [
"X-Session-Id",
]
-# Limits a file-like object to reading only n bytes. Used to keep limit
…
[View More]-# wsgi.input to the Content-Length, otherwise it blocks.
+# Limits a file-like object to reading only n bytes. Used to limit wsgi.input to
+# the Content-Length, otherwise it blocks.
class LimitedReader(object):
def __init__(self, f, n):
self.f = f
[View Less]
commit 56a1f03e14ca15fd5daf1f3c3405c91397e0e4e8
Author: Damian Johnson <atagar(a)torproject.org>
Date: Sat Nov 8 12:11:27 2014 -0800
Using a decorator to support having a default value
Had a eurika moment this last week that we can replace our...
def foo(self, default = UNDEFINED):
try:
... do stuff...
except Exception as exc:
if default != UNDEFINED:
return default
else:
raise exc
...…
[View More] with a decorator...
@with_default
def foo(self, default = UNDEFINED):
... do stuff...
We do this for just about every getter method, so having a decorator
significately cuts down on boilerplate.
---
docs/faq.rst | 2 +-
stem/control.py | 587 ++++++++++++++++++++++++-----------------------------
stem/util/conf.py | 18 +-
3 files changed, 277 insertions(+), 330 deletions(-)
diff --git a/docs/faq.rst b/docs/faq.rst
index aa9b7f9..b235961 100644
--- a/docs/faq.rst
+++ b/docs/faq.rst
@@ -318,7 +318,7 @@ An important thing to note is that a new circuit does not necessarily mean a new
Tor does not have a method for cycling your IP address. This is on purpose, and done for a couple reasons. The first is that this capability is usually requested for not-so-nice reasons such as ban evasion or SEO. Second, repeated circuit creation puts a very high load on the Tor network, so please don't!
-With all that out of the way, how do you create a new circuit? You can customise the rate at which Tor cycles circuits with the **MaxCircuitDirtiness** option in your `torrc <https://www.torproject.org/docs/faq.html.en#torrc>`_. `Vidalia <https://www.torproject.org/getinvolved/volunteer.html.en#project-vidalia>`_ and `arm <https://www.atagar.com/arm/>`_ both provide a method to request a new identity, and you can do so programmatically by sending Tor a NEWNYM signal.
+With all that out of the way, how do you create a new circuit? You can customize the rate at which Tor cycles circuits with the **MaxCircuitDirtiness** option in your `torrc <https://www.torproject.org/docs/faq.html.en#torrc>`_. `Vidalia <https://www.torproject.org/getinvolved/volunteer.html.en#project-vidalia>`_ and `arm <https://www.atagar.com/arm/>`_ both provide a method to request a new identity, and you can do so programmatically by sending Tor a NEWNYM signal.
To do this with telnet...
diff --git a/stem/control.py b/stem/control.py
index c297f0b..d815fcc 100644
--- a/stem/control.py
+++ b/stem/control.py
@@ -222,6 +222,7 @@ If you're fine with allowing your script to raise exceptions then this can be mo
import calendar
import collections
import datetime
+import inspect
import io
import os
import Queue
@@ -357,6 +358,53 @@ AccountingStats = collections.namedtuple('AccountingStats', [
])
+def with_default(yields = False):
+ """
+ Provides a decorator to support having a default value. This should be
+ treated as private.
+ """
+
+ def decorator(func):
+ def get_default(func, args, kwargs):
+ arg_names = inspect.getargspec(func).args
+ default_position = arg_names.index('default') if 'default' in arg_names else None
+
+ if default_position and default_position < len(args):
+ return args[default_position]
+ else:
+ return kwargs.get('default', UNDEFINED)
+
+ if not yields:
+ def wrapped(*args, **kwargs):
+ try:
+ return func(*args, **kwargs)
+ except Exception as exc:
+ default = get_default(func, args, kwargs)
+
+ if default == UNDEFINED:
+ raise exc
+ else:
+ return default
+ else:
+ def wrapped(*args, **kwargs):
+ try:
+ for val in func(*args, **kwargs):
+ yield val
+ except Exception as exc:
+ default = get_default(func, args, kwargs)
+
+ if default == UNDEFINED:
+ raise exc
+ else:
+ if default is not None:
+ for val in default:
+ yield val
+
+ return wrapped
+
+ return decorator
+
+
class BaseController(object):
"""
Controller for the tor process. This is a minimal base class for other
@@ -894,6 +942,7 @@ class Controller(BaseController):
import stem.connection
stem.connection.authenticate(self, *args, **kwargs)
+ @with_default()
def get_info(self, params, default = UNDEFINED, get_bytes = False):
"""
Queries the control socket for the given GETINFO option. If provided a
@@ -950,10 +999,8 @@ class Controller(BaseController):
for param in params:
if param.startswith('ip-to-country/') and self.is_geoip_unavailable():
# the geoip database already looks to be unavailable - abort the request
- if default == UNDEFINED:
- raise stem.ProtocolError('Tor geoip database is unavailable')
- else:
- return default
+
+ raise stem.ProtocolError('Tor geoip database is unavailable')
# if everything was cached then short circuit making the query
if not params:
@@ -1013,11 +1060,9 @@ class Controller(BaseController):
log.debug('GETINFO %s (failed: %s)' % (' '.join(params), exc))
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ raise exc
+ @with_default()
def get_version(self, default = UNDEFINED):
"""
A convenience method to get tor version that current controller is
@@ -1035,20 +1080,15 @@ class Controller(BaseController):
An exception is only raised if we weren't provided a default response.
"""
- try:
- version = self._get_cache('version')
+ version = self._get_cache('version')
- if not version:
- version = stem.version.Version(self.get_info('version'))
- self._set_cache({'version': version})
+ if not version:
+ version = stem.version.Version(self.get_info('version'))
+ self._set_cache({'version': version})
- return version
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ return version
+ @with_default()
def get_exit_policy(self, default = UNDEFINED):
"""
Effective ExitPolicy for our relay. This accounts for
@@ -1067,30 +1107,25 @@ class Controller(BaseController):
"""
with self._msg_lock:
- try:
- config_policy = self._get_cache('exit_policy')
+ config_policy = self._get_cache('exit_policy')
- if not config_policy:
- policy = []
+ if not config_policy:
+ policy = []
- if self.get_conf('ExitPolicyRejectPrivate') == '1':
- policy.append('reject private:*')
+ if self.get_conf('ExitPolicyRejectPrivate') == '1':
+ policy.append('reject private:*')
- for policy_line in self.get_conf('ExitPolicy', multiple = True):
- policy += policy_line.split(',')
+ for policy_line in self.get_conf('ExitPolicy', multiple = True):
+ policy += policy_line.split(',')
- policy += self.get_info('exit-policy/default').split(',')
+ policy += self.get_info('exit-policy/default').split(',')
- config_policy = stem.exit_policy.get_config_policy(policy, self.get_info('address'))
- self._set_cache({'exit_policy': config_policy})
+ config_policy = stem.exit_policy.get_config_policy(policy, self.get_info('address'))
+ self._set_cache({'exit_policy': config_policy})
- return config_policy
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ return config_policy
+ @with_default()
def get_ports(self, listener_type, default = UNDEFINED):
"""
Provides the local ports where tor is listening for the given type of
@@ -1111,14 +1146,9 @@ class Controller(BaseController):
and no default was provided
"""
- try:
- return [port for (addr, port) in self.get_listeners(listener_type) if addr == '127.0.0.1']
- except stem.ControllerError as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ return [port for (addr, port) in self.get_listeners(listener_type) if addr == '127.0.0.1']
+ @with_default()
def get_listeners(self, listener_type, default = UNDEFINED):
"""
Provides the addresses and ports where tor is listening for connections of
@@ -1139,79 +1169,74 @@ class Controller(BaseController):
and no default was provided
"""
+ proxy_addrs = []
+ query = 'net/listeners/%s' % listener_type.lower()
+
try:
- proxy_addrs = []
- query = 'net/listeners/%s' % listener_type.lower()
+ for listener in self.get_info(query).split():
+ if not (listener.startswith('"') and listener.endswith('"')):
+ raise stem.ProtocolError("'GETINFO %s' responses are expected to be quoted: %s" % (query, listener))
+ elif ':' not in listener:
+ raise stem.ProtocolError("'GETINFO %s' had a listener without a colon: %s" % (query, listener))
- try:
- for listener in self.get_info(query).split():
- if not (listener.startswith('"') and listener.endswith('"')):
- raise stem.ProtocolError("'GETINFO %s' responses are expected to be quoted: %s" % (query, listener))
- elif ':' not in listener:
- raise stem.ProtocolError("'GETINFO %s' had a listener without a colon: %s" % (query, listener))
+ listener = listener[1:-1] # strip quotes
+ addr, port = listener.split(':')
- listener = listener[1:-1] # strip quotes
+ # Skip unix sockets, for instance...
+ #
+ # GETINFO net/listeners/control
+ # 250-net/listeners/control="unix:/tmp/tor/socket"
+ # 250 OK
+
+ if addr == 'unix':
+ continue
+
+ proxy_addrs.append((addr, port))
+ except stem.InvalidArguments:
+ # Tor version is old (pre-tor-0.2.2.26-beta), use get_conf() instead.
+ # Some options (like the ORPort) can have optional attributes after the
+ # actual port number.
+
+ port_option = {
+ Listener.OR: 'ORPort',
+ Listener.DIR: 'DirPort',
+ Listener.SOCKS: 'SocksPort',
+ Listener.TRANS: 'TransPort',
+ Listener.NATD: 'NatdPort',
+ Listener.DNS: 'DNSPort',
+ Listener.CONTROL: 'ControlPort',
+ }[listener_type]
+
+ listener_option = {
+ Listener.OR: 'ORListenAddress',
+ Listener.DIR: 'DirListenAddress',
+ Listener.SOCKS: 'SocksListenAddress',
+ Listener.TRANS: 'TransListenAddress',
+ Listener.NATD: 'NatdListenAddress',
+ Listener.DNS: 'DNSListenAddress',
+ Listener.CONTROL: 'ControlListenAddress',
+ }[listener_type]
+
+ port_value = self.get_conf(port_option).split()[0]
+
+ for listener in self.get_conf(listener_option, multiple = True):
+ if ':' in listener:
addr, port = listener.split(':')
-
- # Skip unix sockets, for instance...
- #
- # GETINFO net/listeners/control
- # 250-net/listeners/control="unix:/tmp/tor/socket"
- # 250 OK
-
- if addr == 'unix':
- continue
-
proxy_addrs.append((addr, port))
- except stem.InvalidArguments:
- # Tor version is old (pre-tor-0.2.2.26-beta), use get_conf() instead.
- # Some options (like the ORPort) can have optional attributes after the
- # actual port number.
-
- port_option = {
- Listener.OR: 'ORPort',
- Listener.DIR: 'DirPort',
- Listener.SOCKS: 'SocksPort',
- Listener.TRANS: 'TransPort',
- Listener.NATD: 'NatdPort',
- Listener.DNS: 'DNSPort',
- Listener.CONTROL: 'ControlPort',
- }[listener_type]
-
- listener_option = {
- Listener.OR: 'ORListenAddress',
- Listener.DIR: 'DirListenAddress',
- Listener.SOCKS: 'SocksListenAddress',
- Listener.TRANS: 'TransListenAddress',
- Listener.NATD: 'NatdListenAddress',
- Listener.DNS: 'DNSListenAddress',
- Listener.CONTROL: 'ControlListenAddress',
- }[listener_type]
-
- port_value = self.get_conf(port_option).split()[0]
-
- for listener in self.get_conf(listener_option, multiple = True):
- if ':' in listener:
- addr, port = listener.split(':')
- proxy_addrs.append((addr, port))
- else:
- proxy_addrs.append((listener, port_value))
+ else:
+ proxy_addrs.append((listener, port_value))
- # validate that address/ports are valid, and convert ports to ints
+ # validate that address/ports are valid, and convert ports to ints
- for addr, port in proxy_addrs:
- if not stem.util.connection.is_valid_ipv4_address(addr):
- raise stem.ProtocolError('Invalid address for a %s listener: %s' % (listener_type, addr))
- elif not stem.util.connection.is_valid_port(port):
- raise stem.ProtocolError('Invalid port for a %s listener: %s' % (listener_type, port))
+ for addr, port in proxy_addrs:
+ if not stem.util.connection.is_valid_ipv4_address(addr):
+ raise stem.ProtocolError('Invalid address for a %s listener: %s' % (listener_type, addr))
+ elif not stem.util.connection.is_valid_port(port):
+ raise stem.ProtocolError('Invalid port for a %s listener: %s' % (listener_type, port))
- return [(addr, int(port)) for (addr, port) in proxy_addrs]
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ return [(addr, int(port)) for (addr, port) in proxy_addrs]
+ @with_default()
def get_accounting_stats(self, default = UNDEFINED):
"""
Provides stats related to our relaying limitations if AccountingMax was set
@@ -1239,37 +1264,31 @@ class Controller(BaseController):
and no default was provided
"""
- try:
- if self.get_info('accounting/enabled') != '1':
- raise stem.ControllerError("Accounting isn't enabled")
-
- retrieved = time.time()
- status = self.get_info('accounting/hibernating')
- interval_end = self.get_info('accounting/interval-end')
- used = self.get_info('accounting/bytes')
- left = self.get_info('accounting/bytes-left')
-
- interval_end = datetime.datetime.strptime(interval_end, '%Y-%m-%d %H:%M:%S')
- used_read, used_written = [int(val) for val in used.split(' ', 1)]
- left_read, left_written = [int(val) for val in left.split(' ', 1)]
-
- return AccountingStats(
- retrieved = retrieved,
- status = status,
- interval_end = interval_end,
- time_until_reset = calendar.timegm(interval_end.timetuple()) - int(retrieved),
- read_bytes = used_read,
- read_bytes_left = left_read,
- read_limit = used_read + left_read,
- written_bytes = used_written,
- write_bytes_left = left_written,
- write_limit = used_written + left_written,
- )
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ if self.get_info('accounting/enabled') != '1':
+ raise stem.ControllerError("Accounting isn't enabled")
+
+ retrieved = time.time()
+ status = self.get_info('accounting/hibernating')
+ interval_end = self.get_info('accounting/interval-end')
+ used = self.get_info('accounting/bytes')
+ left = self.get_info('accounting/bytes-left')
+
+ interval_end = datetime.datetime.strptime(interval_end, '%Y-%m-%d %H:%M:%S')
+ used_read, used_written = [int(val) for val in used.split(' ', 1)]
+ left_read, left_written = [int(val) for val in left.split(' ', 1)]
+
+ return AccountingStats(
+ retrieved = retrieved,
+ status = status,
+ interval_end = interval_end,
+ time_until_reset = calendar.timegm(interval_end.timetuple()) - int(retrieved),
+ read_bytes = used_read,
+ read_bytes_left = left_read,
+ read_limit = used_read + left_read,
+ written_bytes = used_written,
+ write_bytes_left = left_written,
+ write_limit = used_written + left_written,
+ )
def get_socks_listeners(self, default = UNDEFINED):
"""
@@ -1290,6 +1309,7 @@ class Controller(BaseController):
return self.get_listeners(Listener.SOCKS, default)
+ @with_default()
def get_protocolinfo(self, default = UNDEFINED):
"""
A convenience method to get the protocol info of the controller.
@@ -1308,15 +1328,9 @@ class Controller(BaseController):
"""
import stem.connection
+ return stem.connection.get_protocolinfo(self)
- try:
- return stem.connection.get_protocolinfo(self)
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
-
+ @with_default()
def get_user(self, default = UNDEFINED):
"""
Provides the user tor is running as. This often only works if tor is
@@ -1344,14 +1358,10 @@ class Controller(BaseController):
if user:
self._set_cache({'user': user})
return user
- elif default == UNDEFINED:
- if self.is_localhost():
- raise ValueError("Unable to resolve tor's user")
- else:
- raise ValueError("Tor isn't running locally")
else:
- return default
+ raise ValueError("Unable to resolve tor's user" if self.is_localhost() else "Tor isn't running locally")
+ @with_default()
def get_pid(self, default = UNDEFINED):
"""
Provides the process id of tor. This often only works if tor is running
@@ -1400,14 +1410,10 @@ class Controller(BaseController):
if pid:
self._set_cache({'pid': pid})
return pid
- elif default == UNDEFINED:
- if self.is_localhost():
- raise ValueError("Unable to resolve tor's pid")
- else:
- raise ValueError("Tor isn't running locally")
else:
- return default
+ raise ValueError("Unable to resolve tor's pid" if self.is_localhost() else "Tor isn't running locally")
+ @with_default()
def get_microdescriptor(self, relay = None, default = UNDEFINED):
"""
Provides the microdescriptor for the relay with the given fingerprint or
@@ -1435,28 +1441,23 @@ class Controller(BaseController):
An exception is only raised if we weren't provided a default response.
"""
- try:
- if relay is None:
- try:
- relay = self.get_info('fingerprint')
- except stem.ControllerError as exc:
- raise stem.ControllerError('Unable to determine our own fingerprint: %s' % exc)
+ if relay is None:
+ try:
+ relay = self.get_info('fingerprint')
+ except stem.ControllerError as exc:
+ raise stem.ControllerError('Unable to determine our own fingerprint: %s' % exc)
- if stem.util.tor_tools.is_valid_fingerprint(relay):
- query = 'md/id/%s' % relay
- elif stem.util.tor_tools.is_valid_nickname(relay):
- query = 'md/name/%s' % relay
- else:
- raise ValueError("'%s' isn't a valid fingerprint or nickname" % relay)
+ if stem.util.tor_tools.is_valid_fingerprint(relay):
+ query = 'md/id/%s' % relay
+ elif stem.util.tor_tools.is_valid_nickname(relay):
+ query = 'md/name/%s' % relay
+ else:
+ raise ValueError("'%s' isn't a valid fingerprint or nickname" % relay)
- desc_content = self.get_info(query, get_bytes = True)
- return stem.descriptor.microdescriptor.Microdescriptor(desc_content)
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ desc_content = self.get_info(query, get_bytes = True)
+ return stem.descriptor.microdescriptor.Microdescriptor(desc_content)
+ @with_default(yields = True)
def get_microdescriptors(self, default = UNDEFINED):
"""
Provides an iterator for all of the microdescriptors that tor presently
@@ -1477,35 +1478,28 @@ class Controller(BaseController):
"""
try:
- try:
- data_directory = self.get_conf('DataDirectory')
- except stem.ControllerError as exc:
- raise stem.OperationFailed(message = 'Unable to determine the data directory (%s)' % exc)
+ data_directory = self.get_conf('DataDirectory')
+ except stem.ControllerError as exc:
+ raise stem.OperationFailed(message = 'Unable to determine the data directory (%s)' % exc)
- cached_descriptor_path = os.path.join(data_directory, 'cached-microdescs')
+ cached_descriptor_path = os.path.join(data_directory, 'cached-microdescs')
- if not os.path.exists(data_directory):
- raise stem.OperationFailed(message = "Data directory reported by tor doesn't exist (%s)" % data_directory)
- elif not os.path.exists(cached_descriptor_path):
- raise stem.OperationFailed(message = "Data directory doens't contain cached microescriptors (%s)" % cached_descriptor_path)
+ if not os.path.exists(data_directory):
+ raise stem.OperationFailed(message = "Data directory reported by tor doesn't exist (%s)" % data_directory)
+ elif not os.path.exists(cached_descriptor_path):
+ raise stem.OperationFailed(message = "Data directory doens't contain cached microescriptors (%s)" % cached_descriptor_path)
- with stem.descriptor.reader.DescriptorReader([cached_descriptor_path]) as reader:
- for desc in reader:
- # It shouldn't be possible for these to be something other than
- # microdescriptors but as the saying goes: trust but verify.
+ with stem.descriptor.reader.DescriptorReader([cached_descriptor_path]) as reader:
+ for desc in reader:
+ # It shouldn't be possible for these to be something other than
+ # microdescriptors but as the saying goes: trust but verify.
- if not isinstance(desc, stem.descriptor.microdescriptor.Microdescriptor):
- raise stem.OperationFailed(message = 'BUG: Descriptor reader provided non-microdescriptor content (%s)' % type(desc))
+ if not isinstance(desc, stem.descriptor.microdescriptor.Microdescriptor):
+ raise stem.OperationFailed(message = 'BUG: Descriptor reader provided non-microdescriptor content (%s)' % type(desc))
- yield desc
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- if default is not None:
- for entry in default:
- yield entry
+ yield desc
+ @with_default()
def get_server_descriptor(self, relay = None, default = UNDEFINED):
"""
Provides the server descriptor for the relay with the given fingerprint or
@@ -1555,14 +1549,12 @@ class Controller(BaseController):
desc_content = self.get_info(query, get_bytes = True)
return stem.descriptor.server_descriptor.RelayDescriptor(desc_content)
except Exception as exc:
- if default == UNDEFINED:
- if not self._is_server_descriptors_available():
- raise ValueError(SERVER_DESCRIPTORS_UNSUPPORTED)
+ if not self._is_server_descriptors_available():
+ raise ValueError(SERVER_DESCRIPTORS_UNSUPPORTED)
- raise exc
- else:
- return default
+ raise exc
+ @with_default(yields = True)
def get_server_descriptors(self, default = UNDEFINED):
"""
Provides an iterator for all of the server descriptors that tor presently
@@ -1583,26 +1575,18 @@ class Controller(BaseController):
default was provided
"""
- try:
- # TODO: We should iterate over the descriptors as they're read from the
- # socket rather than reading the whole thing into memory.
- #
- # https://trac.torproject.org/8248
+ # TODO: We should iterate over the descriptors as they're read from the
+ # socket rather than reading the whole thing into memory.
+ #
+ # https://trac.torproject.org/8248
- desc_content = self.get_info('desc/all-recent', get_bytes = True)
+ desc_content = self.get_info('desc/all-recent', get_bytes = True)
- if not desc_content and not self._is_server_descriptors_available():
- raise ValueError(SERVER_DESCRIPTORS_UNSUPPORTED)
+ if not desc_content and not self._is_server_descriptors_available():
+ raise ValueError(SERVER_DESCRIPTORS_UNSUPPORTED)
- for desc in stem.descriptor.server_descriptor._parse_file(io.BytesIO(desc_content)):
- yield desc
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- if default is not None:
- for entry in default:
- yield entry
+ for desc in stem.descriptor.server_descriptor._parse_file(io.BytesIO(desc_content)):
+ yield desc
def _is_server_descriptors_available(self):
"""
@@ -1612,6 +1596,7 @@ class Controller(BaseController):
return self.get_version() < stem.version.Requirement.MICRODESCRIPTOR_IS_DEFAULT or \
self.get_conf('UseMicrodescriptors', None) == '0'
+ @with_default()
def get_network_status(self, relay = None, default = UNDEFINED):
"""
Provides the router status entry for the relay with the given fingerprint
@@ -1656,32 +1641,27 @@ class Controller(BaseController):
#
# https://trac.torproject.org/7953
- try:
- if relay is None:
- try:
- relay = self.get_info('fingerprint')
- except stem.ControllerError as exc:
- raise stem.ControllerError('Unable to determine our own fingerprint: %s' % exc)
+ if relay is None:
+ try:
+ relay = self.get_info('fingerprint')
+ except stem.ControllerError as exc:
+ raise stem.ControllerError('Unable to determine our own fingerprint: %s' % exc)
- if stem.util.tor_tools.is_valid_fingerprint(relay):
- query = 'ns/id/%s' % relay
- elif stem.util.tor_tools.is_valid_nickname(relay):
- query = 'ns/name/%s' % relay
- else:
- raise ValueError("'%s' isn't a valid fingerprint or nickname" % relay)
+ if stem.util.tor_tools.is_valid_fingerprint(relay):
+ query = 'ns/id/%s' % relay
+ elif stem.util.tor_tools.is_valid_nickname(relay):
+ query = 'ns/name/%s' % relay
+ else:
+ raise ValueError("'%s' isn't a valid fingerprint or nickname" % relay)
- desc_content = self.get_info(query, get_bytes = True)
+ desc_content = self.get_info(query, get_bytes = True)
- if self.get_conf('UseMicrodescriptors', '0') == '1':
- return stem.descriptor.router_status_entry.RouterStatusEntryMicroV3(desc_content)
- else:
- return stem.descriptor.router_status_entry.RouterStatusEntryV3(desc_content)
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ if self.get_conf('UseMicrodescriptors', '0') == '1':
+ return stem.descriptor.router_status_entry.RouterStatusEntryMicroV3(desc_content)
+ else:
+ return stem.descriptor.router_status_entry.RouterStatusEntryV3(desc_content)
+ @with_default(yields = True)
def get_network_statuses(self, default = UNDEFINED):
"""
Provides an iterator for all of the router status entries that tor
@@ -1713,29 +1693,21 @@ class Controller(BaseController):
else:
desc_class = stem.descriptor.router_status_entry.RouterStatusEntryV3
- try:
- # TODO: We should iterate over the descriptors as they're read from the
- # socket rather than reading the whole thing into memory.
- #
- # https://trac.torproject.org/8248
+ # TODO: We should iterate over the descriptors as they're read from the
+ # socket rather than reading the whole thing into memory.
+ #
+ # https://trac.torproject.org/8248
- desc_content = self.get_info('ns/all', get_bytes = True)
+ desc_content = self.get_info('ns/all', get_bytes = True)
- desc_iterator = stem.descriptor.router_status_entry._parse_file(
- io.BytesIO(desc_content),
- True,
- entry_class = desc_class,
- )
+ desc_iterator = stem.descriptor.router_status_entry._parse_file(
+ io.BytesIO(desc_content),
+ True,
+ entry_class = desc_class,
+ )
- for desc in desc_iterator:
- yield desc
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- if default is not None:
- for entry in default:
- yield entry
+ for desc in desc_iterator:
+ yield desc
def get_conf(self, param, default = UNDEFINED, multiple = False):
"""
@@ -2042,6 +2014,7 @@ class Controller(BaseController):
else:
raise stem.ProtocolError('Returned unexpected status code: %s' % response.code)
+ @with_default()
def get_hidden_service_conf(self, default = UNDEFINED):
"""
This provides a mapping of hidden service directories to their
@@ -2084,11 +2057,7 @@ class Controller(BaseController):
(time.time() - start_time))
except stem.ControllerError as exc:
log.debug('GETCONF HiddenServiceOptions (failed: %s)' % exc)
-
- if default != UNDEFINED:
- return default
- else:
- raise exc
+ raise exc
service_dir_map = OrderedDict()
directory = None
@@ -2513,6 +2482,7 @@ class Controller(BaseController):
self._enabled_features += [entry.upper() for entry in features]
+ @with_default()
def get_circuit(self, circuit_id, default = UNDEFINED):
"""
Provides a circuit presently available from tor.
@@ -2529,18 +2499,13 @@ class Controller(BaseController):
An exception is only raised if we weren't provided a default response.
"""
- try:
- for circ in self.get_circuits():
- if circ.id == circuit_id:
- return circ
+ for circ in self.get_circuits():
+ if circ.id == circuit_id:
+ return circ
- raise ValueError("Tor presently does not have a circuit with the id of '%s'" % circuit_id)
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ raise ValueError("Tor presently does not have a circuit with the id of '%s'" % circuit_id)
+ @with_default()
def get_circuits(self, default = UNDEFINED):
"""
Provides tor's currently available circuits.
@@ -2552,21 +2517,15 @@ class Controller(BaseController):
:raises: :class:`stem.ControllerError` if the call fails and no default was provided
"""
- try:
- circuits = []
- response = self.get_info('circuit-status')
+ circuits = []
+ response = self.get_info('circuit-status')
- for circ in response.splitlines():
- circ_message = stem.socket.recv_message(StringIO.StringIO('650 CIRC ' + circ + '\r\n'))
- stem.response.convert('EVENT', circ_message, arrived_at = 0)
- circuits.append(circ_message)
+ for circ in response.splitlines():
+ circ_message = stem.socket.recv_message(StringIO.StringIO('650 CIRC ' + circ + '\r\n'))
+ stem.response.convert('EVENT', circ_message, arrived_at = 0)
+ circuits.append(circ_message)
- return circuits
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ return circuits
def new_circuit(self, path = None, purpose = 'general', await_build = False):
"""
@@ -2728,6 +2687,7 @@ class Controller(BaseController):
else:
raise stem.ProtocolError('CLOSECIRCUIT returned unexpected response code: %s' % response.code)
+ @with_default()
def get_streams(self, default = UNDEFINED):
"""
Provides the list of streams tor is currently handling.
@@ -2740,21 +2700,15 @@ class Controller(BaseController):
provided
"""
- try:
- streams = []
- response = self.get_info('stream-status')
+ streams = []
+ response = self.get_info('stream-status')
- for stream in response.splitlines():
- message = stem.socket.recv_message(StringIO.StringIO('650 STREAM ' + stream + '\r\n'))
- stem.response.convert('EVENT', message, arrived_at = 0)
- streams.append(message)
+ for stream in response.splitlines():
+ message = stem.socket.recv_message(StringIO.StringIO('650 STREAM ' + stream + '\r\n'))
+ stem.response.convert('EVENT', message, arrived_at = 0)
+ streams.append(message)
- return streams
- except Exception as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ return streams
def attach_stream(self, stream_id, circuit_id, exiting_hop = None):
"""
@@ -2870,6 +2824,7 @@ class Controller(BaseController):
return max(0.0, self._last_newnym + 10 - time.time())
+ @with_default()
def get_effective_rate(self, default = UNDEFINED, burst = False):
"""
Provides the maximum rate this relay is configured to relay in bytes per
@@ -2896,18 +2851,12 @@ class Controller(BaseController):
value = None
for attr in attributes:
- try:
- attr_value = int(self.get_conf(attr))
+ attr_value = int(self.get_conf(attr))
- if attr_value == 0 and attr.startswith('Relay'):
- continue # RelayBandwidthRate and RelayBandwidthBurst default to zero
+ if attr_value == 0 and attr.startswith('Relay'):
+ continue # RelayBandwidthRate and RelayBandwidthBurst default to zero
- value = min(value, attr_value) if value else attr_value
- except stem.ControllerError as exc:
- if default == UNDEFINED:
- raise exc
- else:
- return default
+ value = min(value, attr_value) if value else attr_value
return value
@@ -3136,6 +3085,7 @@ def _parse_circ_entry(entry):
return (fingerprint, nickname)
+@with_default()
def _case_insensitive_lookup(entries, key, default = UNDEFINED):
"""
Makes a case insensitive lookup within a list or dictionary, providing the
@@ -3160,7 +3110,4 @@ def _case_insensitive_lookup(entries, key, default = UNDEFINED):
if entry.lower() == key.lower():
return entry
- if default != UNDEFINED:
- return default
- else:
- raise ValueError("key '%s' doesn't exist in dict: %s" % (key, entries))
+ raise ValueError("key '%s' doesn't exist in dict: %s" % (key, entries))
diff --git a/stem/util/conf.py b/stem/util/conf.py
index c96aa88..aafb140 100644
--- a/stem/util/conf.py
+++ b/stem/util/conf.py
@@ -244,9 +244,9 @@ def get_config(handle):
def uses_settings(handle, path, lazy_load = True):
"""
- Provides a function that can be used as an annotation for other functions
- that require settings to be loaded. Functions with this annotation will be
- provided with the configuration as its 'config' keyword argument.
+ Provides a function that can be used as a decorator for other functions that
+ require settings to be loaded. Functions with this decorator will be provided
+ with the configuration as its 'config' keyword argument.
.. versionchanged:: 1.3.0
Omits the 'config' argument if the funcion we're decorating doesn't accept
@@ -262,14 +262,14 @@ def uses_settings(handle, path, lazy_load = True):
:param str handle: hande for the configuration
:param str path: path where the configuration should be loaded from
- :param bool lazy_load: loads the configuration file when the annotation is
+ :param bool lazy_load: loads the configuration file when the decorator is
used if true, otherwise it's loaded right away
- :returns: **function** that can be used as an annotation to provide the
+ :returns: **function** that can be used as a decorator to provide the
configuration
:raises: **IOError** if we fail to read the configuration file, if
- **lazy_load** is true then this arises when we use the annotation
+ **lazy_load** is true then this arises when we use the decorator
"""
config = get_config(handle)
@@ -278,20 +278,20 @@ def uses_settings(handle, path, lazy_load = True):
config.load(path)
config.set('settings_loaded', 'true')
- def annotation(func):
+ def decorator(func):
def wrapped(*args, **kwargs):
if lazy_load and not config.get('settings_loaded', False):
config.load(path)
config.set('settings_loaded', 'true')
- if 'config' in inspect.getargspec(func)[0]:
+ if 'config' in inspect.getargspec(func).args:
return func(*args, config = config, **kwargs)
else:
return func(*args, **kwargs)
return wrapped
- return annotation
+ return decorator
def parse_enum(key, value, enumeration):
[View Less]
commit 15001f5055179999bf51c39591a22558a2a2173d
Author: Translation commit bot <translation(a)torproject.org>
Date: Sat Nov 8 20:15:52 2014 +0000
Update translations for torbutton-torbuttonproperties
---
bg/torbutton.properties | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/bg/torbutton.properties b/bg/torbutton.properties
index 0f95af7..be39b8a 100644
--- a/bg/torbutton.properties
+++ b/bg/torbutton.properties
@@ -12,15 +12,15 @@ …
[View More]torbutton.popup.plugin.warning = Torbutton blocked direct Tor load of plugin con
torbutton.popup.confirm_ca_certs = Torbutton Note: It appears you have no custom Certificate Authorities. Examining the Certificate Authority list is a slow operation and slows down Tor toggle. Would you like to disable the isolation of Certificate Authority certificates? (If you don't understand this, it is safe to click OK)
torbutton.popup.ff3.warning = Warning!\n\nTorbutton on Firefox 3 is known to leak your timezone and livemarks via Tor.\n\nDo you wish to continue anyway?
torbutton.popup.toggle.warning = You need to toggle Tor or restart for your settings to take effect.
-torbutton.popup.test.success = Tor proxy test successful!
+torbutton.popup.test.success = Прокси теста на Tor бе успешен!
torbutton.popup.test.failure = Tor proxy test FAILED! Check your proxy and Polipo settings.
torbutton.popup.test.confirm_toggle = The most recent Tor proxy test failed to use Tor.\n\nAre you sure you want to enable anyway?\n\nNote: If you have fixed the problem, you can rerun the test in the Torbutton Proxy Preferences window to eliminate this warning.
torbutton.popup.test.ff3_notice = Click OK to test Tor proxy settings. This test will happen in the background. Please be patient.
-torbutton.panel.label.verified = Tor Verified
-torbutton.popup.test.auto_failed = The automatic Tor proxy test failed to use Tor.\n\nAre you sure you want to enable anyway?
-torbutton.prefs.recommended = (recommended)
-torbutton.prefs.optional = (optional)
-torbutton.prefs.crucial = (crucial)
+torbutton.panel.label.verified = Потвърдено от Tor
+torbutton.popup.test.auto_failed = Автоматичният Tor прокси тест не успя да използва Tor.\n\nСигурни ли сте,че искате да го включите?
+torbutton.prefs.recommended = (препоръчано)
+torbutton.prefs.optional = (по избор)
+torbutton.prefs.crucial = (решаващо)
torbutton.popup.external.title = Изтегли чужд тип файл?
torbutton.popup.external.app = Тор Браузърът неможе да покаже този файл. Трябва да го отвориш с друга програма.\n\n
torbutton.popup.external.note = Някой типове файлове могат да накарат програми да се свързват към Интернет без да ползват Тор.\n\n
@@ -48,10 +48,10 @@ torbutton.popup.confirm_plugins = Plugins such as Flash can harm your privacy an
torbutton.popup.never_ask_again = Never ask me again
# Canvas permission prompt. Strings are kept here for ease of translation.
-canvas.siteprompt=This website (%S) attempted to extract HTML5 canvas image data, which may be used to uniquely identify your computer.\n\nShould Tor Browser allow this website to extract HTML5 canvas image data?
-canvas.notNow=Not Now
+canvas.siteprompt=Този уебсайт (%S) се опита да извлече HTML5 данните за изображението,което може да бъде използвано,за да може да бъде идентифициран вашият компютър.\n\nДа позволи ли браузърът Tor на този сайт да може да извлича HTML5 данни за изображения?
+canvas.notNow=Не сега
canvas.notNowAccessKey=N
-canvas.allow=Allow in the future
+canvas.allow=Позволи в бъдеще
canvas.allowAccessKey=A
-canvas.never=Never for this site (recommended)
+canvas.never=Никога за този сайт (препоръчано)
canvas.neverAccessKey=e
[View Less]
commit 595568b49176b626e5e5954ca3a0c4e3092813bc
Author: Translation commit bot <translation(a)torproject.org>
Date: Sat Nov 8 19:15:37 2014 +0000
Update translations for tor-launcher-network-settings_completed
---
ro/network-settings.dtd | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/ro/network-settings.dtd b/ro/network-settings.dtd
index b2c90ff..2c6d361 100644
--- a/ro/network-settings.dtd
+++ b/ro/network-settings.dtd
@@ -8,8 +8,8 @@
<!…
[View More]ENTITY torSettings.no "Nu">
<!ENTITY torSettings.firstQuestion "Care din următoarele descrie cel mai bine situația dvs?">
-<!ENTITY torSettings.configurePrompt1 "Conexiunea acestui computer la Internet este cenzurată, filtrată sau cu proxy.">
-<!ENTITY torSettings.configurePrompt2 "Trebuie să configurez punte, firewall sau proxy.">
+<!ENTITY torSettings.configurePrompt1 "Această conexiune la internet a computerului este cenzurată sau prin proxy.">
+<!ENTITY torSettings.configurePrompt2 "Trebuie să configurez puntea sau configurări proxy.">
<!ENTITY torSettings.configure "Configuraţi ">
<!ENTITY torSettings.connectPrompt2 "Vreau sa ma conectez direct la reţeaua Tor .">
<!ENTITY torSettings.connectPrompt3 "Aceasta funcționează în cele mai multe situații.">
@@ -19,9 +19,6 @@
<!-- see https://www.torproject.org/docs/proxychain.html.en -->
<!ENTITY torSettings.proxyHelp "Dacă nu sînteți siguri cum să răspundeți la întrebarea aceasta, uitați-vă la setările de Internet din alt browser pentru a vedea dacă este configurat să folosească un proxy.">
<!ENTITY torSettings.enterProxy "Introduceţi setările pentru proxy .">
-<!ENTITY torSettings.firewallQuestion "Conexiunea la Internet a acestui computer iese printr-un firewall care permite doar conexiuni către anumite porturi?">
-<!ENTITY torSettings.firewallHelp "Dacă nu sînteți sigur cum să răspundeți la această întrebare, alegeți Nu. Dacă întîlniți probleme conectîndu-vă la rețeaua Tor, schimbați aceste setări.">
-<!ENTITY torSettings.enterFirewall "Introduceți o listă separată cu virgule de porturi care sînt permise de firewall.">
<!ENTITY torSettings.bridgeQuestion "Internet Service Providerul (ISP) dvs blochează sau cenzurează conexiunile către rețeaua Tor?">
<!ENTITY torSettings.bridgeHelp "Dacă nu sînteți sigur cum să răspundeți la această întrebare, alegeți Nu.  Dacă alegeți Da, vi se va cere să configurați punțile Tor, care sînt relayuri nelistate care fac mai dificilă blocarea conexiunilor la rețeaua Tor.">
<!ENTITY torSettings.bridgeSettingsPrompt "Puteți folosi setul de punți oferit sau puteți obține și introduce un set particular de punți.">
@@ -59,6 +56,7 @@
<!ENTITY torsettings.bridgeHelp2Heading "Prin Web">
<!ENTITY torsettings.bridgeHelp2 "Folosiți un webbrowser pentru a vizita https://bridges.torproject.org">
<!ENTITY torsettings.bridgeHelp3Heading "Prin email automat">
-<!ENTITY torsettings.bridgeHelp3 "Trimiteți email la bridges(a)torproject.org cu 'get bridges' singură în corpul mesajului.  Oricum, pentru a face mai greu pentru un atacator să afle multe adrese de punți, trebuie să trimiteți această cerere de la o adresă gmail.com sau yahoo.com.">
+<!ENTITY torsettings.bridgeHelp3.emailDesc "Trimiteți email la bridges(a)torproject.org cu linia 'get bridges' inclusă în contextul message.  Totuși, pentru a face dificil unui atacator să învețe cât mai multe adrese de punți, trebuie să trimiteți această cerere de la unul dintre următorii furnizori de servicii email (listați în ordinea preferinței):">
+<!ENTITY torsettings.bridgeHelp3.emailList "https://www.riseup.net, https://mail.google.com, sau https://mail.yahoo.com">
<!ENTITY torsettings.bridgeHelp4Heading "Prin Help Desk">
<!ENTITY torsettings.bridgeHelp4 "Ca o ultimă soluție, puteți cere adrese de punți trimițînd un email politicos la help(a)rt.torproject.org.  Remarcați că o persoană trebuie să răspundă la fiecare cerere.">
[View Less]
commit fdcafc4e6b459eb3c6e03a9e17f790c42e172bb6
Author: Translation commit bot <translation(a)torproject.org>
Date: Sat Nov 8 19:15:33 2014 +0000
Update translations for tor-launcher-network-settings
---
ro/network-settings.dtd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ro/network-settings.dtd b/ro/network-settings.dtd
index 2a3fa1b..2c6d361 100644
--- a/ro/network-settings.dtd
+++ b/ro/network-settings.dtd
@@ -56,7 +56,7 @@
<!ENTITY torsettings.…
[View More]bridgeHelp2Heading "Prin Web">
<!ENTITY torsettings.bridgeHelp2 "Folosiți un webbrowser pentru a vizita https://bridges.torproject.org">
<!ENTITY torsettings.bridgeHelp3Heading "Prin email automat">
-<!ENTITY torsettings.bridgeHelp3.emailDesc "Send email to bridges(a)torproject.org with the line 'get bridges' by itself in the body of the message.  However, to make it harder for an attacker to learn a lot of bridge addresses, you must send this request from one of the following email providers (listed in order of preference):">
+<!ENTITY torsettings.bridgeHelp3.emailDesc "Trimiteți email la bridges(a)torproject.org cu linia 'get bridges' inclusă în contextul message.  Totuși, pentru a face dificil unui atacator să învețe cât mai multe adrese de punți, trebuie să trimiteți această cerere de la unul dintre următorii furnizori de servicii email (listați în ordinea preferinței):">
<!ENTITY torsettings.bridgeHelp3.emailList "https://www.riseup.net, https://mail.google.com, sau https://mail.yahoo.com">
<!ENTITY torsettings.bridgeHelp4Heading "Prin Help Desk">
<!ENTITY torsettings.bridgeHelp4 "Ca o ultimă soluție, puteți cere adrese de punți trimițînd un email politicos la help(a)rt.torproject.org.  Remarcați că o persoană trebuie să răspundă la fiecare cerere.">
[View Less]
commit d3c1beecacf5613efbcfbee9c6bc6e3dabc71bfd
Author: Translation commit bot <translation(a)torproject.org>
Date: Sat Nov 8 10:45:13 2014 +0000
Update translations for whisperback_completed
---
sl_SI/sl_SI.po | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/sl_SI/sl_SI.po b/sl_SI/sl_SI.po
index ba74ef8..eca4d8e 100644
--- a/sl_SI/sl_SI.po
+++ b/sl_SI/sl_SI.po
@@ -9,7 +9,7 @@ msgstr ""
"Project-Id-Version: The Tor Project\n"
"Report-Msgid-Bugs-To: \n"
…
[View More]"POT-Creation-Date: 2014-03-17 17:40+0100\n"
-"PO-Revision-Date: 2014-11-08 10:14+0000\n"
+"PO-Revision-Date: 2014-11-08 10:41+0000\n"
"Last-Translator: Dušan <dusan.k(a)zoho.com>\n"
"Language-Team: Slovenian (Slovenia) (http://www.transifex.com/projects/p/torproject/language/sl_SI/)\n"
"MIME-Version: 1.0\n"
@@ -155,7 +155,7 @@ msgid ""
"\n"
"You should have received a copy of the GNU General Public License\n"
"along with this program. If not, see <http://www.gnu.org/licenses/>.\n"
-msgstr "WhisperBack - Pošljite povratno info v šifrirani pošti\nCopyright (C) 2009-2012 Tails developers <tails(a)boum.org>\n\nTa program je brezplačen; lahko ga delite in/ali prilagodite\npod pogoji GNU General Public License as published by\nthe Free Software Foundation; either version 3 of the License, or (at\nyour option) any later version.\n\nThis program is distributed in the hope that it will be useful, but\nWITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\nGeneral Public License for more details\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see <http://www.gnu.org/licenses/>.\n"
+msgstr "WhisperBack - Pošljite povratno info v šifrirani pošti\nCopyright (C) 2009-2012 Tails developers <tails(a)boum.org>\n\nThis program is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 3 of the License, or (at\nyour option) any later version.\n\nThis program is distributed in the hope that it will be useful, but\nWITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\nGeneral Public License for more details.\n\nMorali bi prejeti kopijo GNU General Public License\nskupaj s tem programom. Če ne, poglejte <http://www.gnu.org/licenses/>.\n"
#: ../data/whisperback.ui.h:20
msgid ""
[View Less]