1 | """Provides common classes and functions most users will want access to.""" |
---|
2 | import threading, sys |
---|
3 | |
---|
4 | class _RequestConfig(object): |
---|
5 | """ |
---|
6 | RequestConfig thread-local singleton |
---|
7 | |
---|
8 | The Routes RequestConfig object is a thread-local singleton that should |
---|
9 | be initialized by the web framework that is utilizing Routes. |
---|
10 | """ |
---|
11 | __shared_state = threading.local() |
---|
12 | |
---|
13 | def __getattr__(self, name): |
---|
14 | return getattr(self.__shared_state, name) |
---|
15 | |
---|
16 | def __setattr__(self, name, value): |
---|
17 | """ |
---|
18 | If the name is environ, load the wsgi envion with load_wsgi_environ |
---|
19 | and set the environ |
---|
20 | """ |
---|
21 | if name == 'environ': |
---|
22 | self.load_wsgi_environ(value) |
---|
23 | return self.__shared_state.__setattr__(name, value) |
---|
24 | return self.__shared_state.__setattr__(name, value) |
---|
25 | |
---|
26 | def __delattr__(self, name): |
---|
27 | delattr(self.__shared_state, name) |
---|
28 | |
---|
29 | def load_wsgi_environ(self, environ): |
---|
30 | """ |
---|
31 | Load the protocol/server info from the environ and store it. |
---|
32 | Also, match the incoming URL if there's already a mapper, and |
---|
33 | store the resulting match dict in mapper_dict. |
---|
34 | """ |
---|
35 | if 'HTTPS' in environ or environ.get('wsgi.url_scheme') == 'https' \ |
---|
36 | or environ.get('HTTP_X_FORWARDED_PROTO') == 'https': |
---|
37 | self.__shared_state.protocol = 'https' |
---|
38 | else: |
---|
39 | self.__shared_state.protocol = 'http' |
---|
40 | try: |
---|
41 | self.mapper.environ = environ |
---|
42 | except AttributeError: |
---|
43 | pass |
---|
44 | |
---|
45 | # Wrap in try/except as common case is that there is a mapper |
---|
46 | # attached to self |
---|
47 | try: |
---|
48 | if 'PATH_INFO' in environ: |
---|
49 | mapper = self.mapper |
---|
50 | path = environ['PATH_INFO'] |
---|
51 | result = mapper.routematch(path) |
---|
52 | if result is not None: |
---|
53 | self.__shared_state.mapper_dict = result[0] |
---|
54 | self.__shared_state.route = result[1] |
---|
55 | else: |
---|
56 | self.__shared_state.mapper_dict = None |
---|
57 | self.__shared_state.route = None |
---|
58 | except AttributeError: |
---|
59 | pass |
---|
60 | |
---|
61 | if 'HTTP_X_FORWARDED_HOST' in environ: |
---|
62 | self.__shared_state.host = environ['HTTP_X_FORWARDED_HOST'] |
---|
63 | elif 'HTTP_HOST' in environ: |
---|
64 | self.__shared_state.host = environ['HTTP_HOST'] |
---|
65 | else: |
---|
66 | self.__shared_state.host = environ['SERVER_NAME'] |
---|
67 | if environ['wsgi.url_scheme'] == 'https': |
---|
68 | if environ['SERVER_PORT'] != '443': |
---|
69 | self.__shared_state.host += ':' + environ['SERVER_PORT'] |
---|
70 | else: |
---|
71 | if environ['SERVER_PORT'] != '80': |
---|
72 | self.__shared_state.host += ':' + environ['SERVER_PORT'] |
---|
73 | |
---|
74 | def request_config(original=False): |
---|
75 | """ |
---|
76 | Returns the Routes RequestConfig object. |
---|
77 | |
---|
78 | To get the Routes RequestConfig: |
---|
79 | |
---|
80 | >>> from routes import * |
---|
81 | >>> config = request_config() |
---|
82 | |
---|
83 | The following attributes must be set on the config object every request: |
---|
84 | |
---|
85 | mapper |
---|
86 | mapper should be a Mapper instance thats ready for use |
---|
87 | host |
---|
88 | host is the hostname of the webapp |
---|
89 | protocol |
---|
90 | protocol is the protocol of the current request |
---|
91 | mapper_dict |
---|
92 | mapper_dict should be the dict returned by mapper.match() |
---|
93 | redirect |
---|
94 | redirect should be a function that issues a redirect, |
---|
95 | and takes a url as the sole argument |
---|
96 | prefix (optional) |
---|
97 | Set if the application is moved under a URL prefix. Prefix |
---|
98 | will be stripped before matching, and prepended on generation |
---|
99 | environ (optional) |
---|
100 | Set to the WSGI environ for automatic prefix support if the |
---|
101 | webapp is underneath a 'SCRIPT_NAME' |
---|
102 | |
---|
103 | Setting the environ will use information in environ to try and |
---|
104 | populate the host/protocol/mapper_dict options if you've already |
---|
105 | set a mapper. |
---|
106 | |
---|
107 | **Using your own requst local** |
---|
108 | |
---|
109 | If you have your own request local object that you'd like to use instead |
---|
110 | of the default thread local provided by Routes, you can configure Routes |
---|
111 | to use it:: |
---|
112 | |
---|
113 | from routes import request_config() |
---|
114 | config = request_config() |
---|
115 | if hasattr(config, 'using_request_local'): |
---|
116 | config.request_local = YourLocalCallable |
---|
117 | config = request_config() |
---|
118 | |
---|
119 | Once you have configured request_config, its advisable you retrieve it |
---|
120 | again to get the object you wanted. The variable you assign to |
---|
121 | request_local is assumed to be a callable that will get the local config |
---|
122 | object you wish. |
---|
123 | |
---|
124 | This example tests for the presence of the 'using_request_local' attribute |
---|
125 | which will be present if you haven't assigned it yet. This way you can |
---|
126 | avoid repeat assignments of the request specific callable. |
---|
127 | |
---|
128 | Should you want the original object, perhaps to change the callable its |
---|
129 | using or stop this behavior, call request_config(original=True). |
---|
130 | """ |
---|
131 | obj = _RequestConfig() |
---|
132 | try: |
---|
133 | if obj.request_local and original is False: |
---|
134 | return getattr(obj, 'request_local')() |
---|
135 | except AttributeError: |
---|
136 | obj.request_local = False |
---|
137 | obj.using_request_local = False |
---|
138 | return _RequestConfig() |
---|
139 | |
---|
140 | from routes.mapper import Mapper |
---|
141 | from routes.util import redirect_to, url_for, URLGenerator |
---|
142 | __all__=['Mapper', 'url_for', 'URLGenerator', 'redirect_to', 'request_config'] |
---|