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__ |
---|