You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					266 lines
				
				9.7 KiB
			
		
		
			
		
	
	
					266 lines
				
				9.7 KiB
			| 
								 
											8 years ago
										 
									 | 
							
								# -*- coding: utf-8 -*-
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								    flask.config
							 | 
						||
| 
								 | 
							
								    ~~~~~~~~~~~~
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Implements the configuration related objects.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :copyright: © 2010 by the Pallets team.
							 | 
						||
| 
								 | 
							
								    :license: BSD, see LICENSE for more details.
							 | 
						||
| 
								 | 
							
								"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import os
							 | 
						||
| 
								 | 
							
								import types
							 | 
						||
| 
								 | 
							
								import errno
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								from werkzeug.utils import import_string
							 | 
						||
| 
								 | 
							
								from ._compat import string_types, iteritems
							 | 
						||
| 
								 | 
							
								from . import json
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class ConfigAttribute(object):
							 | 
						||
| 
								 | 
							
								    """Makes an attribute forward to the config"""
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, name, get_converter=None):
							 | 
						||
| 
								 | 
							
								        self.__name__ = name
							 | 
						||
| 
								 | 
							
								        self.get_converter = get_converter
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __get__(self, obj, type=None):
							 | 
						||
| 
								 | 
							
								        if obj is None:
							 | 
						||
| 
								 | 
							
								            return self
							 | 
						||
| 
								 | 
							
								        rv = obj.config[self.__name__]
							 | 
						||
| 
								 | 
							
								        if self.get_converter is not None:
							 | 
						||
| 
								 | 
							
								            rv = self.get_converter(rv)
							 | 
						||
| 
								 | 
							
								        return rv
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __set__(self, obj, value):
							 | 
						||
| 
								 | 
							
								        obj.config[self.__name__] = value
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class Config(dict):
							 | 
						||
| 
								 | 
							
								    """Works exactly like a dict but provides ways to fill it from files
							 | 
						||
| 
								 | 
							
								    or special dictionaries.  There are two common patterns to populate the
							 | 
						||
| 
								 | 
							
								    config.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Either you can fill the config from a config file::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        app.config.from_pyfile('yourconfig.cfg')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Or alternatively you can define the configuration options in the
							 | 
						||
| 
								 | 
							
								    module that calls :meth:`from_object` or provide an import path to
							 | 
						||
| 
								 | 
							
								    a module that should be loaded.  It is also possible to tell it to
							 | 
						||
| 
								 | 
							
								    use the same module and with that provide the configuration values
							 | 
						||
| 
								 | 
							
								    just before the call::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        DEBUG = True
							 | 
						||
| 
								 | 
							
								        SECRET_KEY = 'development key'
							 | 
						||
| 
								 | 
							
								        app.config.from_object(__name__)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    In both cases (loading from any Python file or loading from modules),
							 | 
						||
| 
								 | 
							
								    only uppercase keys are added to the config.  This makes it possible to use
							 | 
						||
| 
								 | 
							
								    lowercase values in the config file for temporary values that are not added
							 | 
						||
| 
								 | 
							
								    to the config or to define the config keys in the same file that implements
							 | 
						||
| 
								 | 
							
								    the application.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    Probably the most interesting way to load configurations is from an
							 | 
						||
| 
								 | 
							
								    environment variable pointing to a file::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        app.config.from_envvar('YOURAPPLICATION_SETTINGS')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    In this case before launching the application you have to set this
							 | 
						||
| 
								 | 
							
								    environment variable to the file you want to use.  On Linux and OS X
							 | 
						||
| 
								 | 
							
								    use the export statement::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        export YOURAPPLICATION_SETTINGS='/path/to/config/file'
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    On windows use `set` instead.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    :param root_path: path to which files are read relative from.  When the
							 | 
						||
| 
								 | 
							
								                      config object is created by the application, this is
							 | 
						||
| 
								 | 
							
								                      the application's :attr:`~flask.Flask.root_path`.
							 | 
						||
| 
								 | 
							
								    :param defaults: an optional dictionary of default values
							 | 
						||
| 
								 | 
							
								    """
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __init__(self, root_path, defaults=None):
							 | 
						||
| 
								 | 
							
								        dict.__init__(self, defaults or {})
							 | 
						||
| 
								 | 
							
								        self.root_path = root_path
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def from_envvar(self, variable_name, silent=False):
							 | 
						||
| 
								 | 
							
								        """Loads a configuration from an environment variable pointing to
							 | 
						||
| 
								 | 
							
								        a configuration file.  This is basically just a shortcut with nicer
							 | 
						||
| 
								 | 
							
								        error messages for this line of code::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS'])
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param variable_name: name of the environment variable
							 | 
						||
| 
								 | 
							
								        :param silent: set to ``True`` if you want silent failure for missing
							 | 
						||
| 
								 | 
							
								                       files.
							 | 
						||
| 
								 | 
							
								        :return: bool. ``True`` if able to load config, ``False`` otherwise.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        rv = os.environ.get(variable_name)
							 | 
						||
| 
								 | 
							
								        if not rv:
							 | 
						||
| 
								 | 
							
								            if silent:
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            raise RuntimeError('The environment variable %r is not set '
							 | 
						||
| 
								 | 
							
								                               'and as such configuration could not be '
							 | 
						||
| 
								 | 
							
								                               'loaded.  Set this variable and make it '
							 | 
						||
| 
								 | 
							
								                               'point to a configuration file' %
							 | 
						||
| 
								 | 
							
								                               variable_name)
							 | 
						||
| 
								 | 
							
								        return self.from_pyfile(rv, silent=silent)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def from_pyfile(self, filename, silent=False):
							 | 
						||
| 
								 | 
							
								        """Updates the values in the config from a Python file.  This function
							 | 
						||
| 
								 | 
							
								        behaves as if the file was imported as module with the
							 | 
						||
| 
								 | 
							
								        :meth:`from_object` function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param filename: the filename of the config.  This can either be an
							 | 
						||
| 
								 | 
							
								                         absolute filename or a filename relative to the
							 | 
						||
| 
								 | 
							
								                         root path.
							 | 
						||
| 
								 | 
							
								        :param silent: set to ``True`` if you want silent failure for missing
							 | 
						||
| 
								 | 
							
								                       files.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 0.7
							 | 
						||
| 
								 | 
							
								           `silent` parameter.
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        filename = os.path.join(self.root_path, filename)
							 | 
						||
| 
								 | 
							
								        d = types.ModuleType('config')
							 | 
						||
| 
								 | 
							
								        d.__file__ = filename
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            with open(filename, mode='rb') as config_file:
							 | 
						||
| 
								 | 
							
								                exec(compile(config_file.read(), filename, 'exec'), d.__dict__)
							 | 
						||
| 
								 | 
							
								        except IOError as e:
							 | 
						||
| 
								 | 
							
								            if silent and e.errno in (
							 | 
						||
| 
								 | 
							
								                errno.ENOENT, errno.EISDIR, errno.ENOTDIR
							 | 
						||
| 
								 | 
							
								            ):
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            e.strerror = 'Unable to load configuration file (%s)' % e.strerror
							 | 
						||
| 
								 | 
							
								            raise
							 | 
						||
| 
								 | 
							
								        self.from_object(d)
							 | 
						||
| 
								 | 
							
								        return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def from_object(self, obj):
							 | 
						||
| 
								 | 
							
								        """Updates the values from the given object.  An object can be of one
							 | 
						||
| 
								 | 
							
								        of the following two types:
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        -   a string: in this case the object with that name will be imported
							 | 
						||
| 
								 | 
							
								        -   an actual object reference: that object is used directly
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Objects are usually either modules or classes. :meth:`from_object`
							 | 
						||
| 
								 | 
							
								        loads only the uppercase attributes of the module/class. A ``dict``
							 | 
						||
| 
								 | 
							
								        object will not work with :meth:`from_object` because the keys of a
							 | 
						||
| 
								 | 
							
								        ``dict`` are not attributes of the ``dict`` class.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        Example of module-based configuration::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            app.config.from_object('yourapplication.default_config')
							 | 
						||
| 
								 | 
							
								            from yourapplication import default_config
							 | 
						||
| 
								 | 
							
								            app.config.from_object(default_config)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        You should not use this function to load the actual configuration but
							 | 
						||
| 
								 | 
							
								        rather configuration defaults.  The actual config should be loaded
							 | 
						||
| 
								 | 
							
								        with :meth:`from_pyfile` and ideally from a location not within the
							 | 
						||
| 
								 | 
							
								        package because the package might be installed system wide.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        See :ref:`config-dev-prod` for an example of class-based configuration
							 | 
						||
| 
								 | 
							
								        using :meth:`from_object`.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param obj: an import name or object
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        if isinstance(obj, string_types):
							 | 
						||
| 
								 | 
							
								            obj = import_string(obj)
							 | 
						||
| 
								 | 
							
								        for key in dir(obj):
							 | 
						||
| 
								 | 
							
								            if key.isupper():
							 | 
						||
| 
								 | 
							
								                self[key] = getattr(obj, key)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def from_json(self, filename, silent=False):
							 | 
						||
| 
								 | 
							
								        """Updates the values in the config from a JSON file. This function
							 | 
						||
| 
								 | 
							
								        behaves as if the JSON object was a dictionary and passed to the
							 | 
						||
| 
								 | 
							
								        :meth:`from_mapping` function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param filename: the filename of the JSON file.  This can either be an
							 | 
						||
| 
								 | 
							
								                         absolute filename or a filename relative to the
							 | 
						||
| 
								 | 
							
								                         root path.
							 | 
						||
| 
								 | 
							
								        :param silent: set to ``True`` if you want silent failure for missing
							 | 
						||
| 
								 | 
							
								                       files.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 0.11
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        filename = os.path.join(self.root_path, filename)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        try:
							 | 
						||
| 
								 | 
							
								            with open(filename) as json_file:
							 | 
						||
| 
								 | 
							
								                obj = json.loads(json_file.read())
							 | 
						||
| 
								 | 
							
								        except IOError as e:
							 | 
						||
| 
								 | 
							
								            if silent and e.errno in (errno.ENOENT, errno.EISDIR):
							 | 
						||
| 
								 | 
							
								                return False
							 | 
						||
| 
								 | 
							
								            e.strerror = 'Unable to load configuration file (%s)' % e.strerror
							 | 
						||
| 
								 | 
							
								            raise
							 | 
						||
| 
								 | 
							
								        return self.from_mapping(obj)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def from_mapping(self, *mapping, **kwargs):
							 | 
						||
| 
								 | 
							
								        """Updates the config like :meth:`update` ignoring items with non-upper
							 | 
						||
| 
								 | 
							
								        keys.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 0.11
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        mappings = []
							 | 
						||
| 
								 | 
							
								        if len(mapping) == 1:
							 | 
						||
| 
								 | 
							
								            if hasattr(mapping[0], 'items'):
							 | 
						||
| 
								 | 
							
								                mappings.append(mapping[0].items())
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                mappings.append(mapping[0])
							 | 
						||
| 
								 | 
							
								        elif len(mapping) > 1:
							 | 
						||
| 
								 | 
							
								            raise TypeError(
							 | 
						||
| 
								 | 
							
								                'expected at most 1 positional argument, got %d' % len(mapping)
							 | 
						||
| 
								 | 
							
								            )
							 | 
						||
| 
								 | 
							
								        mappings.append(kwargs.items())
							 | 
						||
| 
								 | 
							
								        for mapping in mappings:
							 | 
						||
| 
								 | 
							
								            for (key, value) in mapping:
							 | 
						||
| 
								 | 
							
								                if key.isupper():
							 | 
						||
| 
								 | 
							
								                    self[key] = value
							 | 
						||
| 
								 | 
							
								        return True
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def get_namespace(self, namespace, lowercase=True, trim_namespace=True):
							 | 
						||
| 
								 | 
							
								        """Returns a dictionary containing a subset of configuration options
							 | 
						||
| 
								 | 
							
								        that match the specified namespace/prefix. Example usage::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            app.config['IMAGE_STORE_TYPE'] = 'fs'
							 | 
						||
| 
								 | 
							
								            app.config['IMAGE_STORE_PATH'] = '/var/app/images'
							 | 
						||
| 
								 | 
							
								            app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com'
							 | 
						||
| 
								 | 
							
								            image_store_config = app.config.get_namespace('IMAGE_STORE_')
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        The resulting dictionary `image_store_config` would look like::
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								            {
							 | 
						||
| 
								 | 
							
								                'type': 'fs',
							 | 
						||
| 
								 | 
							
								                'path': '/var/app/images',
							 | 
						||
| 
								 | 
							
								                'base_url': 'http://img.website.com'
							 | 
						||
| 
								 | 
							
								            }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        This is often useful when configuration options map directly to
							 | 
						||
| 
								 | 
							
								        keyword arguments in functions or class constructors.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        :param namespace: a configuration namespace
							 | 
						||
| 
								 | 
							
								        :param lowercase: a flag indicating if the keys of the resulting
							 | 
						||
| 
								 | 
							
								                          dictionary should be lowercase
							 | 
						||
| 
								 | 
							
								        :param trim_namespace: a flag indicating if the keys of the resulting
							 | 
						||
| 
								 | 
							
								                          dictionary should not include the namespace
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								        .. versionadded:: 0.11
							 | 
						||
| 
								 | 
							
								        """
							 | 
						||
| 
								 | 
							
								        rv = {}
							 | 
						||
| 
								 | 
							
								        for k, v in iteritems(self):
							 | 
						||
| 
								 | 
							
								            if not k.startswith(namespace):
							 | 
						||
| 
								 | 
							
								                continue
							 | 
						||
| 
								 | 
							
								            if trim_namespace:
							 | 
						||
| 
								 | 
							
								                key = k[len(namespace):]
							 | 
						||
| 
								 | 
							
								            else:
							 | 
						||
| 
								 | 
							
								                key = k
							 | 
						||
| 
								 | 
							
								            if lowercase:
							 | 
						||
| 
								 | 
							
								                key = key.lower()
							 | 
						||
| 
								 | 
							
								            rv[key] = v
							 | 
						||
| 
								 | 
							
								        return rv
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    def __repr__(self):
							 | 
						||
| 
								 | 
							
								        return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self))
							 |