Package turbogears :: Package filters :: Module base

Source Code for Module turbogears.filters.base

  1  # -*- coding: UTF-8 -*- 
  2  """Standard TurboGears request filters. 
  3   
  4  Provides the following filter classes: 
  5   
  6  ``MonkeyDecodingFilter`` 
  7      For decoding request data 
  8  ``NestedvariablesFilter`` 
  9      For decoding request variables from dotted notation to nested dicts 
 10  ``VirtualPathFilter`` 
 11      For handling dynamic application roots 
 12   
 13  """ 
 14   
 15  __all__ = [ 
 16      'MoneyDecodingFilter', 
 17      'NestedVariablesFilter', 
 18      'VirtualPathFilter', 
 19  ] 
 20   
 21  import logging 
 22   
 23  from cherrypy import NotFound, request 
 24  from cherrypy.filters.basefilter import BaseFilter 
 25   
 26  from turbogears import config 
 27   
 28  from formencode.variabledecode import NestedVariables 
 29   
 30   
 31  log = logging.getLogger('turbogears.filters') 
32 33 34 -class MonkeyDecodingFilter(BaseFilter):
35 """Request filter which decodes the request data according to the encoding 36 specified in the request or the encoding set in the configuration. 37 38 The decoding filter is only used if the configuration setting 39 ``decoding_filter.on`` is set to ``True``. It defaults to `False``. 40 41 The configuration setting ``decoding_filter.encoding`` can be used to force 42 always decoding the request with the given encoding. If this is not set, 43 the encoding will be determined from the request headers. 44 45 If neither of the two is specified, the default encoding is used. This can 46 be set with the ``decoding_filter.default_encoding`` configuration setting. 47 48 Falls back to UTF-8 decoding or ISO-8859-1 for requests with a main content 49 type of "text". Also falls back to ISO-8859-1 if decoding using the 50 requested encoding fails. 51 52 """ 53 @staticmethod
54 - def decode(from_enc):
55 """Recursively decode all values in an iterable from specified encoding. 56 """ 57 58 def decode_from(value, from_enc): 59 if isinstance(value, dict): 60 for k, v in value.items(): 61 value[k] = decode_from(v, from_enc) 62 elif isinstance(value, list): 63 newlist = list() 64 for item in value: 65 newlist.append(decode_from(item, from_enc)) 66 value = newlist 67 elif isinstance(value, str): 68 return value.decode(from_enc) 69 return value
70 71 decoded_params = decode_from(request.params, from_enc) 72 # This is done in two steps to make sure the exception in 73 # before_main can retry a decode with another encoding if needed. 74 # DON'T merge those two lines. 75 request.params = decoded_params
76
77 - def before_main(self):
78 """Decode the request data. 79 80 Substituted for CherryPy's decoding filter in 81 ``startup.startTurboGears()``. 82 83 See class docstring for details. 84 85 """ 86 get = config.get 87 if not get('decoding_filter.on', False): 88 return 89 if getattr(request, "_decoding_attempted", False): 90 return 91 request._decoding_attempted = True 92 encoding = get('decoding_filter.encoding', None) 93 if not encoding: 94 content_type = request.headers.elements("Content-Type") 95 if content_type: 96 content_type = content_type[0] 97 encoding = content_type.params.get("charset", None) 98 if not encoding and content_type.value.lower().startswith("text/"): 99 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1 100 # When no explicit charset parameter is provided by the 101 # sender, media subtypes of the "text" type are defined 102 # to have a default charset value of "ISO-8859-1" when 103 # received via HTTP. 104 encoding = "ISO-8859-1" 105 if not encoding: 106 encoding = get('decoding_filter.default_encoding', "utf-8") 107 try: 108 self.decode(encoding) 109 except UnicodeDecodeError: 110 # IE and Firefox don't supply a charset when submitting form 111 # params with a CT of application/x-www-form-urlencoded. 112 # So after all our guessing, it could *still* be wrong. 113 # Start over with ISO-8859-1, since that seems to be preferred. 114 self.decode("ISO-8859-1")
115
116 117 -class NestedVariablesFilter(BaseFilter):
118 """Request filter that turns request params with names in special dotted 119 notation into nested dictionaries via FormEncode's NestedVariables 120 validator. 121 122 """
123 - def before_main(self):
124 if hasattr(request, 'params'): 125 request.params = NestedVariables.to_python(request.params or {})
126
127 128 -class VirtualPathFilter(BaseFilter):
129 """Filter that makes CherryPy ignorant of a URL root path. 130 131 That is, you can mount your app so the URI "/users/~rdel/myapp/" maps to 132 the root object "/". 133 134 """
135 - def __init__(self, webpath=''):
136 webpath = webpath.rstrip('/') 137 if webpath and not webpath.startswith('/'): 138 webpath = '/' + webpath 139 self.webpath = webpath
140
141 - def before_request_body(self):
142 """Determine the relevant path info by stripping off prefixes. 143 144 Strips webpath and SCRIPT_NAME from request.object_path and 145 sets request.path_info (since CherryPy 2 does not set it). 146 147 """ 148 webpath = self.webpath 149 try: 150 webpath += request.wsgi_environ['SCRIPT_NAME'].rstrip('/') 151 except (AttributeError, KeyError): 152 pass 153 if webpath: 154 if request.object_path.startswith(webpath): 155 request.object_path = request.object_path[len(webpath):] or '/' 156 if request.path.startswith(webpath): 157 request.path_info = request.path[len(webpath):] or '/' 158 else: 159 request.path_info = request.path 160 # check for webpath only if not forwarded 161 try: 162 if not request.wsgi_environ['HTTP_X_FORWARDED_SERVER']: 163 raise KeyError 164 except (AttributeError, KeyError): 165 raise NotFound(request.path) 166 else: 167 request.path_info = request.path
168