Latest Version: 0.9.6.2
/Users/bbangert/Programming/Python/pylons/pylons/configuration.py
0001"""Configuration setup for templating systems and Paste error
0002middleware
0003
0004This module supplies PylonsConfig which handles setting up defaults
0005for templating systems, Paste errorware, and prefixing Routes if
0006necessary.
0007"""
0008import copy
0009import logging
0010import os
0011import warnings
0012
0013from paste.config import DispatchingConfig
0014from paste.deploy.converters import asbool
0015
0016import pylons.legacy
0017import pylons.templating
0018
0019
0020default_template_engine = 'mako'
0021request_defaults = dict(charset='utf-8', errors='replace',
0022                        decode_param_names=False, language='en-us')
0023response_defaults = dict(content_type='text/html',
0024                         charset='utf-8', errors='strict',
0025                         headers={'Cache-Control': 'no-cache',
0026                                  'Pragma': 'no-cache'})
0027
0028log = logging.getLogger(__name__)
0029
0030
0031class PylonsConfig(DispatchingConfig):
0032    """Pylons configuration object
0033
0034    The Pylons configuration object is a per-application instance object
0035    that retains the information regarding the global and app conf's as
0036    well as per-application instance specific data such as the mapper, the
0037    paths for this instance, and the myghty configuration.
0038
0039    The config object is available in your application as the Pylons global
0040    ``pylons.config``. An example usage:
0041
0042    .. code-block :: Python
0043
0044        from pylons import config
0045
0046        template_paths = config['pylons.paths']['templates']
0047
0048    There's several useful keys of the config object most people will be
0049    interested in:
0050
0051    ``pylons.template_options``
0052        Full dict of template options that any TG compatible plugin should
0053        be able to parse. Comes with basic config needed for Myghty, Kid,
0054        and Mako.
0055    ``pylons.paths``
0056        A dict of absolute paths that were defined in the applications
0057        ``config/environment.py`` module.
0058    ``pylons.environ_config``
0059        Dict of environ keys for where in the environ to pickup various
0060        objects for registering with Pylons. If these are present then
0061        PylonsApp will use them from environ rather than using default
0062        middleware from Beaker. Valid keys are: ``session, cache``
0063    ``pylons.template_engines``
0064        List of template engines to configure. The first one in the list will
0065        be configured as the default template engine. Each item in the list is
0066        a dict indicating how to configure the template engine with keys:
0067        ``engine``, ``template_root``, ``template_options``, and ``alias``
0068    ``pylons.default_charset``
0069        Deprecated: Use the response_settings dict instead.
0070        Default character encoding specified to the browser via the
0071        'charset' parameter of the HTTP response's Content-Type header.
0072    ``pylons.strict_c``
0073        Whether or not the ``c`` object should throw an attribute error when
0074        access is attempted to an attribute that doesn't exist.
0075    ``pylons.request_options``
0076        A dict of Content-Type related default settings for new instances of
0077        ``paste.wsgiwrappers.WSGIRequest``. May contain the values ``charset``
0078        and ``errors`` and ``decode_param_names``. Overrides the Pylons default
0079        values specified by the ``request_defaults`` dict.
0080    ``pylons.response_options``
0081        A dict of Content-Type related default settings for new instances of
0082        ``pylons.Response``. May contain the values ``content_type``,
0083        ``charset`` and ``errors``. Overrides the Pylons default values
0084        specified by the ``response_defaults`` dict.
0085    ``routes.map``
0086        Mapper object used for Routing. Yes, it is possible to add routes
0087        after your application has started running.
0088    """
0089    defaults = {
0090        'debug': False,
0091        'pylons.package': None,
0092        'pylons.paths': {'root': None,
0093                         'controllers': None,
0094                         'templates': [],
0095                         'static_files': None},
0096        'pylons.db_engines': {},
0097        'pylons.environ_config': {},
0098        'pylons.g': None,
0099        'pylons.h': None,
0100        'pylons.request_options': request_defaults.copy(),
0101        'pylons.response_options': response_defaults.copy(),
0102        'pylons.strict_c': False,
0103        'buffet.template_engines': [],
0104        'buffet.template_options': {},
0105    }
0106
0107    def __getattr__(self, name):
0108        # Backwards compatibility
0109        if name == 'Config':
0110            class FakeConfig(object):
0111                def __init__(this, *args, **kwargs):
0112                    self.load_environment(*args, **kwargs)
0113                def __getattr__(this, name):
0114                    return getattr(self, name)
0115                def __setattr__(this, name, value):
0116                    setattr(self, name, value)
0117            return FakeConfig
0118        else:
0119            conf_dict = self.current_conf()
0120
0121            # Backwards compat for when the option is now in the dict, and
0122            # access was attempted via attribute
0123            for prefix in ('', 'pylons.', 'buffet.', 'routes.'):
0124                full_name = prefix + name
0125                if full_name in conf_dict:
0126                    warnings.warn(pylons.legacy.config_attr_moved %                                         (name, full_name), DeprecationWarning, 3)
0128                    return conf_dict[full_name]
0129            if name == 'request_defaults':
0130                return request_defaults
0131            elif name == 'response_defaults':
0132                return response_defaults
0133            return getattr(conf_dict, name)
0134
0135    def load_environment(self, tmpl_options=None, map=None, paths=None,
0136                         environ_config=None, default_charset=None,
0137                         strict_c=False, request_settings=None,
0138                         response_settings=None):
0139        """Load the environment options
0140        
0141        Deprecated functionality for pre-0.9.6 projects.
0142        """
0143        warnings.warn(pylons.legacy.config_load_environment,
0144                      DeprecationWarning, 3)
0145
0146        conf = copy.deepcopy(PylonsConfig.defaults)
0147        if tmpl_options:
0148            conf['buffet.template_options'] = tmpl_options
0149
0150        if request_settings:
0151            conf['pylons.request_options'].update(request_settings)
0152
0153        if response_settings:
0154            conf['pylons.response_options'].update(response_settings)
0155
0156        conf['routes.map'] = map
0157        conf['pylons.paths'] = paths or {}
0158        conf['pylons.environ_config'] = environ_config or {}
0159        conf['pylons.strict_c'] = strict_c
0160
0161        if default_charset:
0162            warnings.warn(pylons.legacy.default_charset_warning %                                 dict(klass='Config', charset=default_charset),
0164                          DeprecationWarning, 2)
0165            conf['pylons.response_options']['charset'] = default_charset
0166        self['environment_load'] = conf
0167
0168    def add_template_engine(self, engine, root, options=None, alias=None):
0169        """Add additional template engines for configuration on Pylons WSGI
0170        init.
0171
0172        ``engine``
0173            The name of the template engine
0174
0175        ``root``
0176            Template root for the engine
0177
0178        ``options``
0179            Dict of additional options used during engine initialization, if
0180            not provided, default to using the template_options dict.
0181
0182        ``alias``
0183            Name engine should respond to when actually used. This allows for
0184            multiple configurations of the same engine and lets you alias the
0185            additional ones to other names.
0186
0187        Example of Kid addition:
0188
0189        .. code-block:: Python
0190
0191            # In yourproj/middleware.py
0192            # ...
0193            config.init_app(global_conf, app_conf, package='yourproj')
0194
0195            # Load additional template engines
0196            kidopts = {'kid.assume_encoding':'utf-8', 'kid.encoding':'utf-8'}
0197            config.add_template_engine('kid', 'yourproj.kidtemplates', kidopts)
0198
0199        Example of changing the default template engine:
0200
0201        .. code-block:: Python
0202
0203            # In yourproj/middleware.py
0204            # ...
0205            config.init_app(global_conf, app_conf, package='yourproj')
0206
0207            # Remove existing template engine
0208            old_default = config.template_engines.pop()
0209
0210            # Load additional template engines
0211            kidopts = {'kid.assume_encoding':'utf-8', 'kid.encoding':'utf-8'}
0212            config.add_template_engine('kid', 'yourproj.kidtemplates', kidopts)
0213
0214            # Add old default as additional engine
0215            config.template_engines.append(old_default)
0216        """
0217        if not options:
0218            options = self['buffet.template_options']
0219        config = dict(engine=engine, template_root=root,
0220            template_options=options, alias=alias)
0221        log.debug("Adding %s engine with alias %s and %s options", engine,
0222                  alias, options)
0223        self['buffet.template_engines'].append(config)
0224
0225    def init_app(self, global_conf, app_conf, package=None,
0226                 template_engine=default_template_engine, paths=None):
0227        """Initialize configuration for the application
0228        
0229        .. note
0230            This *must* be called at least once, as soon as possible tosetup 
0231            all the configuration options.
0232        
0233        ``global_config``
0234            Several options are expected to be set for a Pylons web
0235            application. They will be loaded from the global_config which has
0236            the main Paste options. If ``debug`` is not enabled as a global
0237            config option, the following option *must* be set:
0238
0239            * error_to - The email address to send the debug error to
0240
0241            The optional config options in this case are:
0242
0243            * smtp_server - The SMTP server to use, defaults to 'localhost'
0244            * error_log - A logfile to write the error to
0245            * error_subject_prefix - The prefix of the error email subject
0246            * from_address - Whom the error email should be from
0247        ``app_conf``
0248            Defaults supplied via the [app:main] section from the Paste
0249            config file. ``load_config`` only cares about whether a 'prefix'
0250            option is set, if so it will update Routes to ensure URL's take
0251            that into account.
0252        ``package``
0253            The name of the application package, to be stored in the app_conf.
0254        ``template_engine``
0255            Declare the default template engine to setup. Choices are kid,
0256            genshi, mako (the default), and pylonsmyghty.
0257        """
0258        log.debug("Initializing configuration, package: '%s'", package)
0259        conf = global_conf.copy()
0260        conf.update(app_conf)
0261        conf.update(dict(app_conf=app_conf, global_conf=global_conf))
0262        conf.update(self.pop('environment_load', {}))
0263
0264        if paths:
0265            conf['pylons.paths'] = paths
0266
0267        # XXX Legacy: More backwards compatibility locations for the package
0268        #             name
0269        conf['pylons.package'] = conf['package'] =               conf['app_conf']['package'] = package
0271
0272        if 'debug' in conf:
0273            conf['debug'] = asbool(conf['debug'])
0274
0275        if paths and 'root_path' in paths:
0276            warnings.warn(pylons.legacy.root_path, DeprecationWarning, 2)
0277            paths['root'] = paths['root_path']
0278
0279        log.debug("Pushing process configuration")
0280        self.push_process_config(conf)
0281        self.set_defaults(template_engine)
0282
0283    def set_defaults(self, template_engine):
0284        conf = self.current_conf()
0285
0286        # Ensure all the keys from defaults are present, load them if not
0287        for key, val in copy.deepcopy(PylonsConfig.defaults).iteritems():
0288            conf.setdefault(key, val)
0289
0290        # Setup the prefix to override the routes if necessary.
0291        prefix = conf.get('prefix')
0292        if prefix:
0293            warnings.warn(pylons.legacy.prefix_warning % prefix,
0294                          DeprecationWarning, 3)
0295            map = conf.get('routes.map')
0296            if map:
0297                map.prefix = prefix
0298                map._created_regs = False
0299
0300        # Load the errorware configuration from the Paste configuration file
0301        # These all have defaults, and emails are only sent if configured and
0302        # if this application is running in production mode
0303        errorware = {}
0304        errorware['debug'] = asbool(conf.get('debug'))
0305        if not errorware['debug']:
0306            errorware['debug'] = False
0307            errorware['error_email'] = conf.get('email_to')
0308            errorware['error_log'] = conf.get('error_log', None)
0309            errorware['smtp_server'] = conf.get('smtp_server',
0310                'localhost')
0311            errorware['error_subject_prefix'] = conf.get(
0312                'error_subject_prefix', 'WebApp Error: ')
0313            errorware['from_address'] = conf.get(
0314                'from_address', conf.get('error_email_from',
0315                                         'pylons@yourapp.com'))
0316            errorware['error_message'] = conf.get('error_message',
0317                'An internal server error occurred')
0318
0319        # Standard Pylons configuration directives for Myghty
0320        myghty_defaults = {}
0321
0322        # Raise a complete error for the error middleware to catch
0323        myghty_defaults['raise_error'] = True
0324        myghty_defaults['output_encoding'] =               conf['pylons.response_options']['charset']
0326        myghty_defaults['component_root'] = [{os.path.basename(path): path}               for path in conf['pylons.paths']['templates']]
0328
0329        # Merge additional globals
0330        myghty_defaults.setdefault('allow_globals',
0331                                   []).extend(pylons.templating.PYLONS_VARS)
0332
0333        myghty_template_options = {}
0334        if 'myghty_data_dir' in conf:
0335            warnings.warn("Old config option found in ini file, replace "
0336                          "'myghty_data_dir' option with 'data_dir'",
0337                          DeprecationWarning, 3)
0338            myghty_defaults['data_dir'] = conf['myghty_data_dir']
0339        elif 'cache_dir' in conf:
0340            myghty_defaults['data_dir'] = os.path.join(conf['cache_dir'],
0341                'templates')
0342
0343        # Copy in some defaults
0344        if 'cache_dir' in conf:
0345            conf.setdefault('beaker.session.data_dir',
0346                            os.path.join(conf['cache_dir'], 'sessions'))
0347            conf.setdefault('beaker.cache.data_dir',
0348                            os.path.join(conf['cache_dir'], 'cache'))
0349
0350        # Copy Myghty defaults and options into template options
0351        for k, v in myghty_defaults.iteritems():
0352            myghty_template_options['myghty.'+k] = v
0353
0354            # Legacy copy of session and cache settings into conf
0355            if k.startswith('session_') or k.startswith('cache_'):
0356                conf[k] = v
0357
0358        # Copy old session/cache config to new keys for Beaker 0.7+
0359        for key, val in conf.items():
0360            if key.startswith('cache_'):
0361                conf['cache.'+key[6:]] = val
0362            elif key.startswith('session_'):
0363                conf['session.'+key[8:]] = val
0364
0365        # Setup the main template options dict
0366        conf['buffet.template_options'].update(myghty_template_options)
0367
0368        # Setup several defaults for various template languages
0369        defaults = {}
0370
0371        # Rearrange template options as default for Mako
0372        defaults['mako.directories'] = conf['pylons.paths']['templates']
0373        defaults['mako.filesystem_checks'] = True
0374        defaults['mako.output_encoding'] =               conf['pylons.response_options']['charset']
0376        if 'cache_dir' in conf:
0377            defaults['mako.module_directory'] =                   os.path.join(conf['cache_dir'], 'templates')
0379
0380        # Setup kid defaults
0381        defaults['kid.assume_encoding'] = 'utf-8'
0382        defaults['kid.encoding'] = conf['pylons.response_options']['charset']
0383
0384        # Merge template options into defaults
0385        defaults.update(conf['buffet.template_options'])
0386        conf['buffet.template_options'] = defaults
0387
0388        # Prepare our default template engine
0389        if template_engine == 'pylonsmyghty':
0390            self.add_template_engine('pylonsmyghty', None,
0391                                     myghty_template_options)
0392        elif template_engine == 'mako':
0393            self.add_template_engine('mako', '')
0394        elif template_engine in ['genshi', 'kid']:
0395            self.add_template_engine(template_engine,
0396                                     conf['pylons.package'] + '.templates')
0397        elif template_engine == 'cheetah':
0398            self.add_template_engine(template_engine, '%s.templates' %
0399                                     conf['pylons.package'])
0400
0401        log.debug("Loaded %s template engine as the default template renderer", template_engine)
0402
0403        conf['pylons.cache_dir'] = conf.pop('cache_dir',
0404                                            conf['app_conf'].get('cache_dir'))
0405        # Save our errorware values
0406        conf['pylons.errorware'] = errorware
0407
0408
0409config = PylonsConfig()
0410
0411
0412# Push an empty config so all accesses to config at import time have something
0413# to look at and modify. This config will be merged with the app's when it's
0414# built in the paste.app_factory entry point.
0415initial_config = copy.deepcopy(PylonsConfig.defaults)
0416config.push_process_config(initial_config)

Top