Home | History | Annotate | Download | only in paste
      1 # (c) 2006 Ian Bicking, Philip Jenvey and contributors
      2 # Written for Paste (http://pythonpaste.org)
      3 # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
      4 """Paste Configuration Middleware and Objects"""
      5 from paste.registry import RegistryManager, StackedObjectProxy
      6 
      7 __all__ = ['DispatchingConfig', 'CONFIG', 'ConfigMiddleware']
      8 
      9 class DispatchingConfig(StackedObjectProxy):
     10     """
     11     This is a configuration object that can be used globally,
     12     imported, have references held onto.  The configuration may differ
     13     by thread (or may not).
     14 
     15     Specific configurations are registered (and deregistered) either
     16     for the process or for threads.
     17     """
     18     # @@: What should happen when someone tries to add this
     19     # configuration to itself?  Probably the conf should become
     20     # resolved, and get rid of this delegation wrapper
     21 
     22     def __init__(self, name='DispatchingConfig'):
     23         super(DispatchingConfig, self).__init__(name=name)
     24         self.__dict__['_process_configs'] = []
     25 
     26     def push_thread_config(self, conf):
     27         """
     28         Make ``conf`` the active configuration for this thread.
     29         Thread-local configuration always overrides process-wide
     30         configuration.
     31 
     32         This should be used like::
     33 
     34             conf = make_conf()
     35             dispatching_config.push_thread_config(conf)
     36             try:
     37                 ... do stuff ...
     38             finally:
     39                 dispatching_config.pop_thread_config(conf)
     40         """
     41         self._push_object(conf)
     42 
     43     def pop_thread_config(self, conf=None):
     44         """
     45         Remove a thread-local configuration.  If ``conf`` is given,
     46         it is checked against the popped configuration and an error
     47         is emitted if they don't match.
     48         """
     49         self._pop_object(conf)
     50 
     51     def push_process_config(self, conf):
     52         """
     53         Like push_thread_config, but applies the configuration to
     54         the entire process.
     55         """
     56         self._process_configs.append(conf)
     57 
     58     def pop_process_config(self, conf=None):
     59         self._pop_from(self._process_configs, conf)
     60 
     61     def _pop_from(self, lst, conf):
     62         popped = lst.pop()
     63         if conf is not None and popped is not conf:
     64             raise AssertionError(
     65                 "The config popped (%s) is not the same as the config "
     66                 "expected (%s)"
     67                 % (popped, conf))
     68 
     69     def _current_obj(self):
     70         try:
     71             return super(DispatchingConfig, self)._current_obj()
     72         except TypeError:
     73             if self._process_configs:
     74                 return self._process_configs[-1]
     75             raise AttributeError(
     76                 "No configuration has been registered for this process "
     77                 "or thread")
     78     current = current_conf = _current_obj
     79 
     80 CONFIG = DispatchingConfig()
     81 
     82 no_config = object()
     83 class ConfigMiddleware(RegistryManager):
     84     """
     85     A WSGI middleware that adds a ``paste.config`` key (by default)
     86     to the request environment, as well as registering the
     87     configuration temporarily (for the length of the request) with
     88     ``paste.config.CONFIG`` (or any other ``DispatchingConfig``
     89     object).
     90     """
     91 
     92     def __init__(self, application, config, dispatching_config=CONFIG,
     93                  environ_key='paste.config'):
     94         """
     95         This delegates all requests to `application`, adding a *copy*
     96         of the configuration `config`.
     97         """
     98         def register_config(environ, start_response):
     99             popped_config = environ.get(environ_key, no_config)
    100             current_config = environ[environ_key] = config.copy()
    101             environ['paste.registry'].register(dispatching_config,
    102                                                current_config)
    103 
    104             try:
    105                 app_iter = application(environ, start_response)
    106             finally:
    107                 if popped_config is no_config:
    108                     environ.pop(environ_key, None)
    109                 else:
    110                     environ[environ_key] = popped_config
    111             return app_iter
    112 
    113         super(self.__class__, self).__init__(register_config)
    114 
    115 def make_config_filter(app, global_conf, **local_conf):
    116     conf = global_conf.copy()
    117     conf.update(local_conf)
    118     return ConfigMiddleware(app, conf)
    119 
    120 make_config_middleware = ConfigMiddleware.__doc__
    121