Package turbogears :: Module controllers

Source Code for Module turbogears.controllers

  1  """Classes and methods for TurboGears controllers.""" 
  2   
  3  import logging 
  4  import re 
  5  import urllib 
  6  import urlparse 
  7  import types 
  8  from itertools import izip 
  9  from dispatch import generic, strategy, functions 
 10  from inspect import isclass 
 11  import cherrypy 
 12  from cherrypy import request, response 
 13  import turbogears.util as tg_util 
 14  from turbogears import view, database, errorhandling, config 
 15  from turbogears.decorator import weak_signature_decorator 
 16  from turbogears.validators import Invalid 
 17  from turbogears.errorhandling import error_handler, exception_handler 
 18   
 19   
 20  log = logging.getLogger("turbogears.controllers") 
 21   
 22   
 23  if config.get("session_filter.on", None): 
 24      if config.get("session_filter.storage_type", None) == "PostgreSQL": 
 25          import psycopg2 
 26          config.update( 
 27                  {'session_filter.get_db': psycopg2.connect( 
 28                      psycopg2.get('sessions.postgres.dsn')) 
 29                      }) 
 30      # support for mysql/sqlite/etc here 
 31   
 32   
33 -def _process_output(output, template, format, content_type, 34 mapping, fragment=False):
35 """Produce final output form from data returned from a controller method. 36 37 See the expose() arguments for more info since they are the same. 38 39 """ 40 if isinstance(output, dict): 41 from turbogears.widgets import js_location 42 43 css = tg_util.setlike() 44 js = dict(izip(js_location, iter(tg_util.setlike, None))) 45 include_widgets = {} 46 include_widgets_lst = config.get("tg.include_widgets", []) 47 48 if config.get("tg.mochikit_all", False): 49 include_widgets_lst.insert(0, 'turbogears.mochikit') 50 51 for i in include_widgets_lst: 52 widget = tg_util.load_class(i) 53 if isclass(widget): 54 widget = widget() 55 include_widgets["tg_%s" % i.split(".")[-1]] = widget 56 for script in widget.retrieve_javascript(): 57 if hasattr(script, "location"): 58 js[script.location].add(script) 59 else: 60 js[js_location.head].add(script) 61 css.add_all(widget.retrieve_css()) 62 63 for value in output.itervalues(): 64 if hasattr(value, "retrieve_css"): 65 retrieve = getattr(value, "retrieve_css") 66 if callable(retrieve): 67 css.add_all(value.retrieve_css()) 68 if hasattr(value, "retrieve_javascript"): 69 retrieve = getattr(value, "retrieve_javascript") 70 if callable(retrieve): 71 for script in value.retrieve_javascript(): 72 if hasattr(script, "location"): 73 js[script.location].add(script) 74 else: 75 js[js_location.head].add(script) 76 output.update(include_widgets) 77 output["tg_css"] = css 78 for location in iter(js_location): 79 output["tg_js_%s" % str(location)] = js[location] 80 81 tg_flash = _get_flash() 82 if tg_flash: 83 output["tg_flash"] = tg_flash 84 85 headers = {'Content-Type': content_type} 86 output = view.render(output, template=template, format=format, 87 mapping=mapping, headers=headers, 88 fragment=fragment) 89 content_type = headers['Content-Type'] 90 91 if content_type: 92 response.headers["Content-Type"] = content_type 93 else: 94 content_type = response.headers.get("Content-Type", 'text/plain') 95 96 if content_type.startswith("text/"): 97 if isinstance(output, unicode): 98 output = output.encode(tg_util.get_template_encoding_default()) 99 100 return output
101 102
103 -class BadFormatError(Exception):
104 """Output-format exception."""
105 106
107 -def validate(form=None, validators=None, 108 failsafe_schema=errorhandling.FailsafeSchema.none, 109 failsafe_values=None, state_factory=None):
110 """Validate input. 111 112 @param form: a form instance that must be passed throught the validation 113 process... you must give a the same form instance as the one that will 114 be used to post data on the controller you are putting the validate 115 decorator on. 116 @type form: a form instance 117 118 @param validators: individual validators to use for parameters. 119 If you use a schema for validation then the schema instance must 120 be the sole argument. 121 If you use simple validators, then you must pass a dictionary with 122 each value name to validate as a key of the dictionary and the validator 123 instance (eg: tg.validators.Int() for integer) as the value. 124 @type validators: dictionary or schema instance 125 126 @param failsafe_schema: a schema for handling failsafe values. 127 The default is 'none', but you can also use 'values', 'map_errors', 128 or 'defaults' to map erroneous inputs to values, corresponding exceptions 129 or method defaults. 130 @type failsafe_schema: errorhandling.FailsafeSchema 131 132 @param failsafe_values: replacements for erroneous inputs. You can either 133 define replacements for every parameter, or a single replacement value 134 for all parameters. This is only used when failsafe_schema is 'values'. 135 @type failsafe_values: a dictionary or a single value 136 137 @param state_factory: If this is None, the initial state for validation 138 is set to None, otherwise this must be a callable that returns the initial 139 state to be used for validation. 140 @type state_factory: callable or None 141 142 """ 143 def entangle(func): 144 if callable(form) and not hasattr(form, "validate"): 145 init_form = lambda self: form(self) 146 else: 147 init_form = lambda self: form 148 149 def validate(func, *args, **kw): 150 # do not validate a second time if already validated 151 if hasattr(request, 'validation_state'): 152 return func(*args, **kw) 153 154 form = init_form(args and args[0] or kw["self"]) 155 args, kw = tg_util.to_kw(func, args, kw) 156 157 errors = {} 158 if state_factory is not None: 159 state = state_factory() 160 else: 161 state = None 162 163 if form: 164 value = kw.copy() 165 try: 166 kw.update(form.validate(value, state)) 167 except Invalid, e: 168 errors = e.unpack_errors() 169 request.validation_exception = e 170 request.validated_form = form 171 172 if validators: 173 if isinstance(validators, dict): 174 for field, validator in validators.iteritems(): 175 try: 176 kw[field] = validator.to_python( 177 kw.get(field, None), state) 178 except Invalid, error: 179 errors[field] = error 180 else: 181 try: 182 value = kw.copy() 183 kw.update(validators.to_python(value, state)) 184 except Invalid, e: 185 errors = e.unpack_errors() 186 request.validation_exception = e 187 request.validation_errors = errors 188 request.input_values = kw.copy() 189 request.validation_state = state 190 191 if errors: 192 kw = errorhandling.dispatch_failsafe(failsafe_schema, 193 failsafe_values, errors, func, kw) 194 args, kw = tg_util.from_kw(func, args, kw) 195 return errorhandling.run_with_errors(errors, func, *args, **kw)
196 197 return validate 198 return weak_signature_decorator(entangle) 199 200
201 -class CustomDispatch(functions.GenericFunction):
202
203 - def combine(self, cases):
204 strict = [strategy.ordered_signatures, strategy.safe_methods] 205 cases = strategy.separate_qualifiers( 206 cases, 207 primary = strict, 208 ) 209 primary = strategy.method_chain(cases.get('primary', [])) 210 if type(primary) != types.FunctionType: 211 for i in primary: 212 for y in i: 213 return y[1] 214 return primary
215 216
217 -def _add_rule(_expose, found_default, as_format, accept_format, template, 218 rulefunc):
219 if as_format == "default": 220 if found_default: 221 colon = template.find(":") 222 if colon == -1: 223 as_format = template 224 else: 225 as_format = template[:colon] 226 else: 227 found_default = True 228 ruleparts = ['kw.get("tg_format", "default") == "%s"' % as_format] 229 if accept_format: 230 ruleparts.append('(accept == "%s" and kw.get("tg_format"' 231 ', "default") == "default")' % accept_format) 232 rule = " or ".join(ruleparts) 233 log.debug("Generated rule %s", rule) 234 _expose.when(rule)(rulefunc) 235 236 return found_default
237 238
239 -def _build_rules(func):
240 [generic(CustomDispatch)] 241 def _expose(func, accept, allow_json, *args, **kw): 242 pass
243 244 if func._allow_json: 245 log.debug("Adding allow_json rule: " 246 'allow_json and (kw.get("tg_format", None) == "json"' 247 ' or accept in ("application/json", "text/javascript"))') 248 _expose.when('allow_json and (kw.get("tg_format", None) == "json"' 249 ' or accept in ("application/json", "text/javascript"))')( 250 lambda _func, accept, allow_json, *args, **kw: 251 _execute_func(_func, "json", "json", "application/json", 252 None, False, args, kw)) 253 254 found_default = False 255 for ruleinfo in func._ruleinfo: 256 found_default = _add_rule(_expose, found_default, **ruleinfo) 257 258 func._expose = _expose 259 260
261 -def expose(template=None, validators=None, allow_json=None, html=None, 262 format=None, content_type=None, inputform=None, fragment=False, 263 as_format="default", mapping=None, accept_format=None):
264 """Exposes a method to the web. 265 266 By putting the expose decorator on a method, you tell TurboGears that 267 the method should be accessible via URL traversal. Additionally, expose 268 handles the output processing (turning a dictionary into finished 269 output) and is also responsible for ensuring that the request is 270 wrapped in a database transaction. 271 272 You can apply multiple expose decorators to a method, if 273 you'd like to support multiple output formats. The decorator that's 274 listed first in your code without as_format or accept_format is 275 the default that is chosen when no format is specifically asked for. 276 Any other expose calls that are missing as_format and accept_format 277 will have as_format implicitly set to the whatever comes before 278 the ":" in the template name (or the whole template name if there 279 is no ":". For example, <code>expose("json")</code>, if it's not 280 the default expose, will have as_format set to "json". 281 282 When as_format is set, passing the same value in the tg_format 283 parameter in a request will choose the options for that expose 284 decorator. Similarly, accept_format will watch for matching 285 Accept headers. You can also use both. expose("json", as_format="json", 286 accept_format="application/json") will choose JSON output for either 287 case: tg_format=json as a parameter or Accept: application/json as a 288 request header. 289 290 Passing allow_json=True to an expose decorator 291 is equivalent to adding the decorator just mentioned. 292 293 Each expose decorator has its own set of options, and each one 294 can choose a different template or even template engine (you can 295 use Kid for HTML output and Cheetah for plain text, for example). 296 See the other expose parameters below to learn about the options 297 you can pass to the template engine. 298 299 Take a look at the 300 <a href="tests/test_expose-source.html">test_expose.py</a> suite 301 for more examples. 302 303 @param template "templateengine:dotted.reference" reference along the 304 Python path for the template and the template engine. For 305 example, "kid:foo.bar" will have Kid render the bar template in 306 the foo package. 307 @keyparam format format for the template engine to output (if the 308 template engine can render different formats. Kid, for example, 309 can render "html", "xml" or "xhtml") 310 @keyparam content_type sets the content-type http header 311 @keyparam allow_json allow the function to be exposed as json 312 @keyparam fragment for template engines (like Kid) that generate 313 DOCTYPE declarations and the like, this is a signal to 314 just generate the immediate template fragment. Use this 315 if you're building up a page from multiple templates or 316 going to put something onto a page with .innerHTML. 317 @keyparam mapping mapping with options that are sent to the template 318 engine 319 @keyparam as_format designates which value of tg_format will choose 320 this expose. 321 @keyparam accept_format which value of an Accept: header will 322 choose this expose. 323 @keyparam html deprecated in favor of template 324 @keyparam validators deprecated. Maps argument names to validator 325 applied to that arg 326 @keyparam inputform deprecated. A form object that generates the 327 input to this method 328 329 """ 330 if html: 331 template = html 332 if not template: 333 template = format 334 if format == "json" or (format is None and