[3] | 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'] |
---|