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