Source code for staticconf.readers

"""
Functions to read values directly from a
:class:`staticconf.config.ConfigNamespace`.  Values will be validated and
cast to the requested type.


Examples
--------

.. code-block:: python

    import staticconf

    # read an int
    max_cycles = staticconf.read_int('max_cycles')
    start_id = staticconf.read_int('poller.init.start_id', default=0)

    # start_date will be a datetime.date
    start_date = staticconf.read_date('start_date')

    # matcher will be a regex object
    matcher = staticconf.read_regex('matcher_pattern')

    # Read a value from a different namespace
    intervals = staticconf.read_float('intervals', namespace='something')


Readers can be attached to a namespace using a :class:`NamespaceReaders`
object.

.. code-block:: python

    import staticconf

    bling_reader = staticconf.NamespaceReaders('bling')

    # These values are read from the `bling` ConfigNamespace
    currency = bling_reader.read_string('currency')
    value = bling_reader.read_float('value')


Arguments
---------

Readers accept the following kwargs:

config_key
    string configuration key using dotted notation
default
    if no `default` is given, the key must be present in the configuration.
    If the key is missing a :class:`staticconf.errors.ConfigurationError`
    is raised.
namespace
    get the value from this namespace instead of DEFAULT.


Building custom readers
-----------------------
:func:`build_reader` is a factory function which can be used for creating
custom readers from a validation function.  A validation function should handle
all exceptions and raise a :class:`staticconf.errors.ValidationError` if there
is a problem.

First create a validation function

.. code-block:: python

    def validate_currency(value):
        try:
            # Assume a tuple or a list
            name, decimal_points = value
            return Currency(name, decimal_points)
        except Exception, e:
            raise ValidationErrror(...)


Example of a custom reader:

.. code-block:: python

    from staticconf import readers

    read_currency = readers.build_reader(validate_currency)

    # Returns a Currency object using the data from the config namespace
    # at they key `currencies.usd`.
    usd_currency = read_currency('currencies.usd')


"""
from staticconf import validation, config, errors
from staticconf.proxy import UndefToken


def _read_config(config_key, config_namespace, default):
    value = config_namespace.get(config_key, default=default)
    if value is UndefToken:
        msg = '%s missing value for %s' % (config_namespace, config_key)
        raise errors.ConfigurationError(msg)
    return value


[docs]def build_reader(validator, reader_namespace=config.DEFAULT): """A factory method for creating a custom config reader from a validation function. :param validator: a validation function which acceptance one argument (the configuration value), and returns that value casted to the appropriate type. :param reader_namespace: the default namespace to use. Defaults to `DEFAULT`. """ def reader(config_key, default=UndefToken, namespace=None): config_namespace = config.get_namespace(namespace or reader_namespace) return validator(_read_config(config_key, config_namespace, default)) return reader
class ReaderNameFactory(object): @staticmethod def get_name(name): return 'read_%s' % name if name else 'read' @staticmethod def get_list_of_name(name): return 'read_list_of_%s' % name def get_all_accessors(name_factory): for name, validator in validation.get_validators(): yield name_factory.get_name(name), validator yield (name_factory.get_list_of_name(name), validation.build_list_type_validator(validator)) class NamespaceAccessor(object): def __init__(self, name, accessor_map, builder): self.accessor_map = accessor_map self.builder = builder self.namespace = name def __getattr__(self, item): if item not in self.accessor_map: raise AttributeError(item) return self.builder(self.accessor_map[item], self.namespace) def get_methods(self): return dict((name, getattr(self, name)) for name in self.accessor_map) def build_accessor_type(name_factory, builder): accessor_map = dict(get_all_accessors(name_factory)) return lambda name: NamespaceAccessor(name, accessor_map, builder) NamespaceReaders = build_accessor_type(ReaderNameFactory, build_reader) """An object with all reader functions which retrieve configuration from a named namespace, instead of `DEFAULT`. """ default_readers = NamespaceReaders(config.DEFAULT) globals().update(default_readers.get_methods()) __all__ = ['NamespaceReaders'] + list(default_readers.get_methods())