Skip to content

Commit

Permalink
Move config functionality to new bridgedb.configure module.
Browse files Browse the repository at this point in the history
 * MOVE bridgedb.persistent.Conf → bridgedb.configure.Conf.
 * MOVE bridgedb.Main.loadConfig → bridgedb.configure.loadConfig.
 * CHANGE imports in bridgedb.Main and bridgedb.persistent to use class
   and function in bridgedb.configure.
 * CHANGE Sphinx documentation to point to new locations.
  • Loading branch information
isislovecruft committed Aug 27, 2014
1 parent 02b94eb commit ca88624
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 118 deletions.
109 changes: 2 additions & 107 deletions lib/bridgedb/Main.py
Expand Up @@ -25,6 +25,8 @@
from bridgedb import safelog
from bridgedb import schedule
from bridgedb import util
from bridgedb.configure import loadConfig
from bridgedb.configure import Conf
from bridgedb.parse import options

import bridgedb.Bridges as Bridges
Expand Down Expand Up @@ -179,113 +181,6 @@ def updateBridgeHistory(bridges, timestamps):
state.save()
return

def loadConfig(configFile=None, configCls=None):
"""Load configuration settings on top of the current settings.
All pathnames and filenames within settings in the ``configFile`` will be
expanded, and their expanded values will be stored in the returned
:class:`config <Conf>` object.
** Note: **
On the strange-looking use of
``exec compile(open(configFile).read(), '<string>', 'exec') in dict()``
in this function:
The contents of the config file should be compiled first, and then
``exec``ed -- not ``execfile``! -- in order to get the contents of the
config file to exist within the scope of the configuration dictionary.
Otherwise, Python *will* default_ to executing the config file directly
within the ``globals()`` scope.
Additionally, it's roughly 20-30 times faster_ to use the ``compile``
builtin on a string (the contents of the file) before ``exec``ing it, than
using ``execfile`` directly on the file.
.. _default: http://stackoverflow.com/q/17470193
.. _faster: http://lucumr.pocoo.org/2011/2/1/exec-in-python/
:ivar boolean itsSafeToUseLogging: This is called in :func:`startup`
before :func:`safelog.configureLogging`. When called from ``startup``,
the ``configCls`` parameter is not given, because that is the first
time that a :class:`Conf` is created. If a :class:`logging.Logger` is
created in this function, then logging will not be correctly
configured, therefore, if the ``configCls`` parameter is not given,
then it's the first time this function has been called and it is
therefore not safe to make calls to the logging module.
:type: configFile: string or None
:param string configFile: If given, the filename of the config file to
load.
:type configCls: :class:`bridgedb.Main.Conf` or None
:param configCls: The current configuration, if one already exists.
:rtype: :class:`Conf`
:returns: A new configuration, with the old settings as defaults, and the
settings from the config file overriding them.
"""
itsSafeToUseLogging = False
configuration = {}

if configCls:
itsSafeToUseLogging = True
oldConfig = configCls.__dict__
configuration.update(**oldConfig) # Load current settings
logging.info("Reloading over in-memory configurations...")

conffile = configFile
if (configFile is None) and ('CONFIG_FILE' in configuration):
conffile = configuration['CONFIG_FILE']

if conffile is not None:
if itsSafeToUseLogging:
logging.info("Loading settings from config file: '%s'" % conffile)
compiled = compile(open(conffile).read(), '<string>', 'exec')
exec compiled in configuration

if itsSafeToUseLogging:
logging.debug("New configuration settings:")
logging.debug("\n".join(["{0} = {1}".format(key, value)
for key, value in configuration.items()]))

# Create a :class:`Conf` from the settings stored within the local scope
# of the ``configuration`` dictionary:
config = persistent.Conf(**configuration)

# We want to set the updated/expanded paths for files on the ``config``,
# because the copy of this config, `state.config` is used later to compare
# with a new :class:`Conf` instance, to see if there were any changes.
#
# See :meth:`bridgedb.persistent.State.useUpdatedSettings`.

for attr in ["PROXY_LIST_FILES", "BRIDGE_FILES", "EXTRA_INFO_FILES"]:
setting = getattr(config, attr, None)
if setting is None:
setattr(config, attr, []) # If they weren't set, make them lists
else:
setattr(config, attr, # If they were set, expand the paths:
[os.path.abspath(os.path.expanduser(f)) for f in setting])

for attr in ["DB_FILE", "DB_LOG_FILE", "MASTER_KEY_FILE", "PIDFILE",
"ASSIGNMENTS_FILE", "HTTPS_CERT_FILE", "HTTPS_KEY_FILE",
"LOG_FILE", "STATUS_FILE", "COUNTRY_BLOCK_FILE",
"GIMP_CAPTCHA_DIR", "GIMP_CAPTCHA_HMAC_KEYFILE",
"GIMP_CAPTCHA_RSA_KEYFILE"]:
setting = getattr(config, attr, None)
if setting is None:
setattr(config, attr, setting)
else:
setattr(config, attr, os.path.abspath(os.path.expanduser(setting)))

for attr in ["FORCE_PORTS", "FORCE_FLAGS"]:
setting = getattr(config, attr, []) # Default to empty lists
setattr(config, attr, setting)

for domain in config.EMAIL_DOMAINS:
config.EMAIL_DOMAIN_MAP[domain] = domain

if conffile: # Store the pathname of the config file, if one was used
config.CONFIG_FILE = os.path.abspath(os.path.expanduser(conffile))

return config

def loadProxyList(cfg):
ipset = {}
for fname in cfg.PROXY_LIST_FILES:
Expand Down
131 changes: 131 additions & 0 deletions lib/bridgedb/configure.py
@@ -0,0 +1,131 @@
# -*- coding: utf-8 ; test-case-name: bridgedb.test.test_Main -*-
#
# This file is part of BridgeDB, a Tor bridge distribution system.
#
# :authors: please see the AUTHORS file for attributions
# :copyright: (c) 2013-2014, Isis Lovecruft
# (c) 2013-2014, Matthew Finkel
# (c) 2007-2014, Nick Mathewson
# (c) 2007-2014, The Tor Project, Inc.
# :license: see LICENSE for licensing information

"""Utilities for dealing with configuration files for BridgeDB."""

import logging


def loadConfig(configFile=None, configCls=None):
"""Load configuration settings on top of the current settings.
All pathnames and filenames within settings in the ``configFile`` will be
expanded, and their expanded values will be stored in the returned
:class:`config <Conf>` object.
** Note: **
On the strange-looking use of
``exec compile(open(configFile).read(), '<string>', 'exec') in dict()``
in this function:
The contents of the config file should be compiled first, and then
``exec``ed -- not ``execfile``! -- in order to get the contents of the
config file to exist within the scope of the configuration dictionary.
Otherwise, Python *will* default_ to executing the config file directly
within the ``globals()`` scope.
Additionally, it's roughly 20-30 times faster_ to use the ``compile``
builtin on a string (the contents of the file) before ``exec``ing it, than
using ``execfile`` directly on the file.
.. _default: http://stackoverflow.com/q/17470193
.. _faster: http://lucumr.pocoo.org/2011/2/1/exec-in-python/
:ivar boolean itsSafeToUseLogging: This is called in :func:`startup`
before :func:`safelog.configureLogging`. When called from ``startup``,
the ``configCls`` parameter is not given, because that is the first
time that a :class:`Conf` is created. If a :class:`logging.Logger` is
created in this function, then logging will not be correctly
configured, therefore, if the ``configCls`` parameter is not given,
then it's the first time this function has been called and it is
therefore not safe to make calls to the logging module.
:type: configFile: string or None
:param string configFile: If given, the filename of the config file to
load.
:type configCls: :class:`bridgedb.Main.Conf` or None
:param configCls: The current configuration, if one already exists.
:rtype: :class:`Conf`
:returns: A new configuration, with the old settings as defaults, and the
settings from the config file overriding them.
"""
itsSafeToUseLogging = False
configuration = {}

if configCls:
itsSafeToUseLogging = True
oldConfig = configCls.__dict__
configuration.update(**oldConfig) # Load current settings
logging.info("Reloading over in-memory configurations...")

conffile = configFile
if (configFile is None) and ('CONFIG_FILE' in configuration):
conffile = configuration['CONFIG_FILE']

if conffile is not None:
if itsSafeToUseLogging:
logging.info("Loading settings from config file: '%s'" % conffile)
compiled = compile(open(conffile).read(), '<string>', 'exec')
exec compiled in configuration

if itsSafeToUseLogging:
logging.debug("New configuration settings:")
logging.debug("\n".join(["{0} = {1}".format(key, value)
for key, value in configuration.items()]))

# Create a :class:`Conf` from the settings stored within the local scope
# of the ``configuration`` dictionary:
config = Conf(**configuration)

# We want to set the updated/expanded paths for files on the ``config``,
# because the copy of this config, `state.config` is used later to compare
# with a new :class:`Conf` instance, to see if there were any changes.
#
# See :meth:`bridgedb.persistent.State.useUpdatedSettings`.

for attr in ["PROXY_LIST_FILES", "BRIDGE_FILES", "EXTRA_INFO_FILES"]:
setting = getattr(config, attr, None)
if setting is None:
setattr(config, attr, []) # If they weren't set, make them lists
else:
setattr(config, attr, # If they were set, expand the paths:
[os.path.abspath(os.path.expanduser(f)) for f in setting])

for attr in ["DB_FILE", "DB_LOG_FILE", "MASTER_KEY_FILE", "PIDFILE",
"ASSIGNMENTS_FILE", "HTTPS_CERT_FILE", "HTTPS_KEY_FILE",
"LOG_FILE", "STATUS_FILE", "COUNTRY_BLOCK_FILE",
"GIMP_CAPTCHA_DIR", "GIMP_CAPTCHA_HMAC_KEYFILE",
"GIMP_CAPTCHA_RSA_KEYFILE"]:
setting = getattr(config, attr, None)
if setting is None:
setattr(config, attr, setting)
else:
setattr(config, attr, os.path.abspath(os.path.expanduser(setting)))

for attr in ["FORCE_PORTS", "FORCE_FLAGS"]:
setting = getattr(config, attr, []) # Default to empty lists
setattr(config, attr, setting)

for domain in config.EMAIL_DOMAINS:
config.EMAIL_DOMAIN_MAP[domain] = domain

if conffile: # Store the pathname of the config file, if one was used
config.CONFIG_FILE = os.path.abspath(os.path.expanduser(conffile))

return config


class Conf(object):
"""A configuration object. Holds unvalidated attributes."""
def __init__(self, **attrs):
for key, value in attrs.items():
if key == key.upper():
if not key.startswith('__'):
self.__dict__[key] = value
14 changes: 3 additions & 11 deletions lib/bridgedb/persistent.py
Expand Up @@ -24,6 +24,7 @@
from twisted.spread import jelly

from bridgedb import Filters, Bridges, Dist
from bridgedb.configure import Conf
#from bridgedb.proxy import ProxySet

_state = None
Expand Down Expand Up @@ -77,15 +78,6 @@ def load(stateCls=None):
return loaded


class Conf(object):
"""A configuration object. Holds unvalidated attributes."""
def __init__(self, **attrs):
for key, value in attrs.items():
if key == key.upper():
if not key.startswith('__'):
self.__dict__[key] = value


class State(jelly.Jellyable):
"""Pickled, jellied storage container for persistent state."""

Expand Down Expand Up @@ -235,7 +227,7 @@ def useChangedSettings(self, config):
"""Take a new config, compare it to the last one, and update settings.
Given a ``config`` object created from the configuration file, compare
it to the last :class:`~bridgedb.Main.Conf` that was stored, and apply
it to the last :class:`~bridgedb.configure.Conf` that was stored, and apply
any settings which were changed to be attributes of the :class:`State`
instance.
"""
Expand All @@ -249,7 +241,7 @@ def useChangedSettings(self, config):
#
# Be sure, when updating settings while parsing the config
# file, to assign the new settings as attributes of the
# :class:`bridgedb.Main.Conf` instance.
# :class:`bridgedb.configure.Conf` instance.
if value != self.config.__dict__[key]:
setattr(self, key, value)
updated.append(key)
Expand Down

0 comments on commit ca88624

Please sign in to comment.