summaryrefslogtreecommitdiff
path: root/paste/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'paste/config.py')
-rw-r--r--paste/config.py120
1 files changed, 120 insertions, 0 deletions
diff --git a/paste/config.py b/paste/config.py
new file mode 100644
index 0000000..c531579
--- /dev/null
+++ b/paste/config.py
@@ -0,0 +1,120 @@
+# (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__