summaryrefslogtreecommitdiff
path: root/paste
diff options
context:
space:
mode:
authorianb <devnull@localhost>2005-08-26 22:53:02 +0000
committerianb <devnull@localhost>2005-08-26 22:53:02 +0000
commitd33a98eaa07f8c8e2b2a298a14637196719745ab (patch)
tree34038ebff3156876cc5224ab7dcbdd5ce9e98733 /paste
parent3775900de2bbc989cdfb466b3704fed2bbf3abd3 (diff)
downloadpastedeploy-d33a98eaa07f8c8e2b2a298a14637196719745ab.tar.gz
(re)Added a configuration middleware
Diffstat (limited to 'paste')
-rw-r--r--paste/deploy/__init__.py1
-rw-r--r--paste/deploy/config.py163
2 files changed, 164 insertions, 0 deletions
diff --git a/paste/deploy/__init__.py b/paste/deploy/__init__.py
index 5c58ac1..5c83a6d 100644
--- a/paste/deploy/__init__.py
+++ b/paste/deploy/__init__.py
@@ -1,2 +1,3 @@
from loadwsgi import loadapp, loadfilter, loadserver
+from config import CONFIG
diff --git a/paste/deploy/config.py b/paste/deploy/config.py
new file mode 100644
index 0000000..4b6a2d7
--- /dev/null
+++ b/paste/deploy/config.py
@@ -0,0 +1,163 @@
+from paste.util.threadinglocal import local
+import threading
+# Loaded lazily
+wsgilib = None
+
+__all__ = ['DispatchingConfig', 'CONFIG', 'ConfigMiddleware']
+
+config_local = local()
+
+def local_dict():
+ try:
+ return config_local.wsgi_dict
+ except AttributeError:
+ config_local.wsgi_dict = result = {}
+ return result
+
+class DispatchingConfig(object):
+
+ """
+ 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
+
+ _constructor_lock = threading.Lock()
+
+ def __init__(self):
+ self._constructor_lock.acquire()
+ try:
+ self.dispatching_id = 0
+ while 1:
+ self._local_key = 'paste.processconfig_%i' % self.dispatching_id
+ if not local_dict().has_key(self._local_key):
+ break
+ self.dispatching_id += 1
+ finally:
+ self._constructor_lock.release()
+ self._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)
+ """
+ local_dict().setdefault(self._local_key, []).append(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_from(local_dict()[self._local_key], 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 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 __getattr__(self, attr):
+ conf = self.current_conf()
+ if not conf:
+ raise AttributeError(
+ "No configuration has been registered for this process "
+ "or thread")
+ return getattr(conf, attr)
+
+ def current_conf(self):
+ thread_configs = local_dict().get(self._local_key)
+ if thread_configs:
+ return thread_configs[-1]
+ elif self._process_configs:
+ return self._process_configs[-1]
+ else:
+ return None
+
+ def __getitem__(self, key):
+ # I thought __getattr__ would catch this, but apparently not
+ conf = self.current_conf()
+ if not conf:
+ raise TypeError(
+ "No configuration has been registered for this process "
+ "or thread")
+ return conf[key]
+
+CONFIG = DispatchingConfig()
+
+class ConfigMiddleware(object):
+
+ """
+ A WSGI middleware that adds a ``paste.config`` key to the request
+ environment, as well as registering the configuration temporarily
+ (for the length of the request) with ``paste.CONFIG``.
+ """
+
+ def __init__(self, application, config):
+ """
+ This delegates all requests to `application`, adding a *copy*
+ of the configuration `config`.
+ """
+ self.application = application
+ self.config = config
+
+ def __call__(self, environ, start_response):
+ global wsgilib
+ if wsgilib is None:
+ from paste import wsgilib
+ conf = environ['paste.config'] = self.config.copy()
+ app_iter = None
+ CONFIG.push_thread_config(conf)
+ try:
+ app_iter = self.application(environ, start_response)
+ finally:
+ if app_iter is None:
+ # An error occurred...
+ CONFIG.pop_thread_config(conf)
+ if type(app_iter) in (list, tuple):
+ # Because it is a concrete iterator (not a generator) we
+ # know the configuration for this thread is no longer
+ # needed:
+ CONFIG.pop_thread_config(conf)
+ return app_iter
+ else:
+ def close_config():
+ CONFIG.pop_thread_config(conf)
+ new_app_iter = wsgilib.add_close(app_iter, close_config)
+ return new_app_iter
+
+def make_config_filter(app, global_conf, **local_conf):
+ conf = global_conf.copy()
+ conf.update(local_conf)
+ return ConfigMiddleware(app, conf)
+