121 lines
4.2 KiB
Python
121 lines
4.2 KiB
Python
# (c) 2006 Ian Bicking, Philip Jenvey and contributors
|
|
# Written for Paste (http://pythonpaste.org)
|
|
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
|
"""Paste Configuration Middleware and Objects"""
|
|
from paste.registry import RegistryManager, StackedObjectProxy
|
|
|
|
__all__ = ['DispatchingConfig', 'CONFIG', 'ConfigMiddleware']
|
|
|
|
class DispatchingConfig(StackedObjectProxy):
|
|
"""
|
|
This is a configuration object that can be used globally,
|
|
imported, have references held onto. The configuration may differ
|
|
by thread (or may not).
|
|
|
|
Specific configurations are registered (and deregistered) either
|
|
for the process or for threads.
|
|
"""
|
|
# @@: What should happen when someone tries to add this
|
|
# configuration to itself? Probably the conf should become
|
|
# resolved, and get rid of this delegation wrapper
|
|
|
|
def __init__(self, name='DispatchingConfig'):
|
|
super(DispatchingConfig, self).__init__(name=name)
|
|
self.__dict__['_process_configs'] = []
|
|
|
|
def push_thread_config(self, conf):
|
|
"""
|
|
Make ``conf`` the active configuration for this thread.
|
|
Thread-local configuration always overrides process-wide
|
|
configuration.
|
|
|
|
This should be used like::
|
|
|
|
conf = make_conf()
|
|
dispatching_config.push_thread_config(conf)
|
|
try:
|
|
... do stuff ...
|
|
finally:
|
|
dispatching_config.pop_thread_config(conf)
|
|
"""
|
|
self._push_object(conf)
|
|
|
|
def pop_thread_config(self, conf=None):
|
|
"""
|
|
Remove a thread-local configuration. If ``conf`` is given,
|
|
it is checked against the popped configuration and an error
|
|
is emitted if they don't match.
|
|
"""
|
|
self._pop_object(conf)
|
|
|
|
def push_process_config(self, conf):
|
|
"""
|
|
Like push_thread_config, but applies the configuration to
|
|
the entire process.
|
|
"""
|
|
self._process_configs.append(conf)
|
|
|
|
def pop_process_config(self, conf=None):
|
|
self._pop_from(self._process_configs, conf)
|
|
|
|
def _pop_from(self, lst, conf):
|
|
popped = lst.pop()
|
|
if conf is not None and popped is not conf:
|
|
raise AssertionError(
|
|
"The config popped (%s) is not the same as the config "
|
|
"expected (%s)"
|
|
% (popped, conf))
|
|
|
|
def _current_obj(self):
|
|
try:
|
|
return super(DispatchingConfig, self)._current_obj()
|
|
except TypeError:
|
|
if self._process_configs:
|
|
return self._process_configs[-1]
|
|
raise AttributeError(
|
|
"No configuration has been registered for this process "
|
|
"or thread")
|
|
current = current_conf = _current_obj
|
|
|
|
CONFIG = DispatchingConfig()
|
|
|
|
no_config = object()
|
|
class ConfigMiddleware(RegistryManager):
|
|
"""
|
|
A WSGI middleware that adds a ``paste.config`` key (by default)
|
|
to the request environment, as well as registering the
|
|
configuration temporarily (for the length of the request) with
|
|
``paste.config.CONFIG`` (or any other ``DispatchingConfig``
|
|
object).
|
|
"""
|
|
|
|
def __init__(self, application, config, dispatching_config=CONFIG,
|
|
environ_key='paste.config'):
|
|
"""
|
|
This delegates all requests to `application`, adding a *copy*
|
|
of the configuration `config`.
|
|
"""
|
|
def register_config(environ, start_response):
|
|
popped_config = environ.get(environ_key, no_config)
|
|
current_config = environ[environ_key] = config.copy()
|
|
environ['paste.registry'].register(dispatching_config,
|
|
current_config)
|
|
|
|
try:
|
|
app_iter = application(environ, start_response)
|
|
finally:
|
|
if popped_config is no_config:
|
|
environ.pop(environ_key, None)
|
|
else:
|
|
environ[environ_key] = popped_config
|
|
return app_iter
|
|
|
|
super(self.__class__, self).__init__(register_config)
|
|
|
|
def make_config_filter(app, global_conf, **local_conf):
|
|
conf = global_conf.copy()
|
|
conf.update(local_conf)
|
|
return ConfigMiddleware(app, conf)
|
|
|
|
make_config_middleware = ConfigMiddleware.__doc__
|