Open Table Of Contents

Source code for bridgedb.configure

# -*- coding: utf-8 ; test-case-name: bridgedb.test.test_configure -*-
#
# This file is part of BridgeDB, a Tor bridge distribution system.
#
# :authors: please see the AUTHORS file for attributions
# :copyright: (c) 2013-2015, Isis Lovecruft
#             (c) 2013-2015, Matthew Finkel
#             (c) 2007-2015, Nick Mathewson
#             (c) 2007-2015, The Tor Project, Inc.
# :license: see LICENSE for licensing information

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

import logging
import os

# Used to set the SUPPORTED_TRANSPORTS:
from bridgedb import strings


[docs]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:`configuration <bridgedb.configure.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 passed to ``exec()`` -- 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 passing it to ``exec()``, 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 bool itsSafeToUseLogging: This is called in :func:`bridgedb.Main.run` before :func:`bridgedb.safelog.configureLogging`. When called from :func:`~bridgedb.Main.run`, the **configCls** parameter is not given, because that is the first time that a :class:`config <bridgedb.configure.Conf>` has been 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: :any:`str` or ``None`` :param configFile: If given, the filename of the config file to load. :type configCls: :class:`bridgedb.configure.Conf` or ``None`` :param configCls: The current configuration instance, if one already exists. :returns: A new :class:`configuration <bridgedb.configure.Conf>`, with the old settings as defaults, and the settings from the **configFile** (if given) overriding those defaults. """ 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() if not key.startswith('_')])) # 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: # pragma: no cover 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", "EMAIL_GPG_HOMEDIR", "EMAIL_GPG_PASSPHRASE_FILE"]: 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 ["HTTPS_ROTATION_PERIOD", "EMAIL_ROTATION_PERIOD"]: setting = getattr(config, attr, None) # Default to None setattr(config, attr, setting) for attr in ["IGNORE_NETWORKSTATUS"]: setting = getattr(config, attr, True) # Default to True setattr(config, attr, setting) for attr in ["FORCE_PORTS", "FORCE_FLAGS", "NO_DISTRIBUTION_COUNTRIES"]: setting = getattr(config, attr, []) # Default to empty lists setattr(config, attr, setting) for attr in ["SUPPORTED_TRANSPORTS"]: setting = getattr(config, attr, {}) # Default to empty dicts setattr(config, attr, setting) # Set the SUPPORTED_TRANSPORTS to populate the webserver and email options: strings._setSupportedTransports(getattr(config, "SUPPORTED_TRANSPORTS", {})) strings._setDefaultTransport(getattr(config, "DEFAULT_TRANSPORT", "")) logging.info("Currently supported transports: %s" % " ".join(strings._getSupportedTransports())) logging.info("Default transport: %s" % strings._getDefaultTransport()) 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
[docs]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