def deco(f): if isclass(f): for method in [m[1] for m in getmembers(f) if ismethod(m[1])]: if getattr(method, 'exposed', False): _cfg(method)['transactional'] = True _cfg(method)['transactional_ignore_redirects'] = _cfg(method).get('transactional_ignore_redirects', ignore_redirects) else: _cfg(f)['transactional'] = True _cfg(f)['transactional_ignore_redirects'] = ignore_redirects return f
def accept_noncanonical(func): ''' Flags a controller method as accepting non-canoncial URLs. ''' _cfg(func)['accept_noncanonical'] = True return func
def accept_noncanonical(func): """ Flags a controller method as accepting non-canoncial URLs. """ _cfg(func)["accept_noncanonical"] = True return func
def route(self, node, path): ''' Looks up a controller from a node based upon the specified path. :param node: The node, such as a root controller object. :param path: The path to look up on this node. ''' path = path.split('/')[1:] try: node, remainder = lookup_controller(node, path) return node, remainder except NonCanonicalPath, e: if self.force_canonical and \ not _cfg(e.controller).get('accept_noncanonical', False): if request.method == 'POST': raise RuntimeError( "You have POSTed to a URL '%s' which " "requires a slash. Most browsers will not maintain " "POST data when redirected. Please update your code " "to POST to '%s/' or set force_canonical to False" % (request.pecan['routing_path'], request.pecan['routing_path']) ) redirect(code=302, add_slash=True) return e.controller, e.remainder
def on_error(self, state, e): # # If we should ignore redirects, # (e.g., shouldn't consider them rollback-worthy) # don't set `state.request.error = True`. # transactional_ignore_redirects = state.request.method not in ('GET', 'HEAD') if state.controller is not None: transactional_ignore_redirects = _cfg(state.controller).get('transactional_ignore_redirects', transactional_ignore_redirects) if type(e) is HTTPFound and transactional_ignore_redirects is True: return state.request.error = True
def determine_hooks(self, controller=None): ''' Determines the hooks to be run, in which order. :param controller: If specified, includes hooks for a specific controller. ''' controller_hooks = [] if controller: controller_hooks = _cfg(controller).get('hooks', []) return list( sorted( chain(controller_hooks, self.hooks), lambda x,y: cmp(x.priority, y.priority) ) )
def __init__(self, location=None, errors={}): if hasattr(state, 'controller'): cfg = _cfg(state.controller) else: cfg = {} if location is None and 'error_handler' in cfg: location = cfg['error_handler'] if callable(location): location = location() merge_dicts(request.pecan['validation_errors'], errors) if 'pecan.params' not in request.environ: request.environ['pecan.params'] = dict(request.str_params) request.environ['pecan.validation_errors'] = request.pecan['validation_errors'] if cfg.get('htmlfill') is not None: request.environ['pecan.htmlfill'] = cfg['htmlfill'] request.environ['REQUEST_METHOD'] = 'GET' ForwardRequestException.__init__(self, location)
def after(self, state): if state.request.transactional: if state.request.error: self.rollback() else: self.commit() # # If a controller was routed to, find any # after_commit actions it may have registered, and perform # them. # controller = getattr(state, 'controller', None) if controller is not None: actions = _cfg(controller).get('after_commit', []) for action in actions: action() self.clear()
def is_transactional(self, state): ''' Decide if a request should be wrapped in a transaction, based upon the state of the request. By default, wraps all but ``GET`` and ``HEAD`` requests in a transaction, along with respecting the ``transactional`` decorator from :mod:pecan.decorators. :param state: The Pecan state object for the current request. ''' controller = getattr(state, 'controller', None) if controller: force_transactional = _cfg(controller).get('transactional', False) else: force_transactional = False if state.request.method not in ('GET', 'HEAD') or force_transactional: return True return False
def decorate(f): # flag the method as exposed f.exposed = True # set a "pecan" attribute, where we will store details cfg = _cfg(f) cfg["content_type"] = content_type cfg.setdefault("template", []).append(template) cfg.setdefault("content_types", {})[content_type] = template # handle generic controllers if generic: cfg["generic"] = True cfg["generic_handlers"] = dict(DEFAULT=f) f.when = when_for(f) # store the arguments for this controller method cfg["argspec"] = getargspec(f) # store the schema cfg["error_handler"] = error_handler if schema is not None: cfg["schema"] = schema cfg["validate_json"] = False elif json_schema is not None: cfg["schema"] = json_schema cfg["validate_json"] = True # store the variable decode configuration if isinstance(variable_decode, dict) or variable_decode == True: _variable_decode = dict(dict_char=".", list_char="-") if isinstance(variable_decode, dict): _variable_decode.update(variable_decode) cfg["variable_decode"] = _variable_decode # store the htmlfill configuration if isinstance(htmlfill, dict) or htmlfill == True or schema is not None: _htmlfill = dict(auto_insert_errors=False) if isinstance(htmlfill, dict): _htmlfill.update(htmlfill) cfg["htmlfill"] = _htmlfill return f
def decorate(f): # flag the method as exposed f.exposed = True # set a "pecan" attribute, where we will store details cfg = _cfg(f) cfg['content_type'] = content_type cfg.setdefault('template', []).append(template) cfg.setdefault('content_types', {})[content_type] = template # handle generic controllers if generic: cfg['generic'] = True cfg['generic_handlers'] = dict(DEFAULT=f) f.when = when_for(f) # store the arguments for this controller method cfg['argspec'] = getargspec(f) return f
def after(self, state): if state.request.transactional: action_name = None if state.request.error: action_name = "after_rollback" self.rollback() else: action_name = "after_commit" self.commit() # # If a controller was routed to, find any # after_* actions it may have registered, and perform # them. # if action_name: controller = getattr(state, "controller", None) if controller is not None: actions = _cfg(controller).get(action_name, []) for action in actions: action() self.clear()
def _unlocked_method(func): _cfg(func)['secured'] = Any return func
def handle_request(self): ''' The main request handler for Pecan applications. ''' # get a sorted list of hooks, by priority (no controller hooks yet) state.hooks = self.determine_hooks() # store the routing path for the current application to allow hooks to # modify it request.pecan['routing_path'] = request.path_info # handle "on_route" hooks self.handle_hooks('on_route', state) # lookup the controller, respecting content-type as requested # by the file extension on the URI path = request.pecan['routing_path'] request.pecan['extension'] = None # attempt to guess the content type based on the file extension if self.guess_content_type_from_ext \ and not request.pecan['content_type'] \ and '.' in path.split('/')[-1]: new_path, extension = splitext(path) # preface with a letter to ensure compat for 2.5 potential_type = guess_type('x' + extension)[0] if potential_type is not None: path = new_path request.pecan['extension'] = extension request.pecan['content_type'] = potential_type controller, remainder = self.route(self.root, path) cfg = _cfg(controller) if cfg.get('generic_handler'): raise exc.HTTPNotFound # handle generic controllers im_self = None if cfg.get('generic'): im_self = controller.im_self handlers = cfg['generic_handlers'] controller = handlers.get(request.method, handlers['DEFAULT']) cfg = _cfg(controller) # add the controller to the state so that hooks can use it state.controller = controller # if unsure ask the controller for the default content type if not request.pecan['content_type']: # attempt to find a best match based on accept headers (if they # exist) if 'Accept' in request.headers: best_default = acceptparse.MIMEAccept( request.headers['Accept'] ).best_match( cfg.get('content_types', {}).keys() ) if best_default is None: msg = "Controller '%s' defined does not support " + \ "content_type '%s'. Supported type(s): %s" logger.error( msg % ( controller.__name__, request.pecan['content_type'], cfg.get('content_types', {}).keys() ) ) raise exc.HTTPNotAcceptable() request.pecan['content_type'] = best_default else: request.pecan['content_type'] = cfg.get( 'content_type', 'text/html' ) elif cfg.get('content_type') is not None and \ request.pecan['content_type'] not in \ cfg.get('content_types', {}): msg = "Controller '%s' defined does not support content_type " + \ "'%s'. Supported type(s): %s" logger.error( msg % ( controller.__name__, request.pecan['content_type'], cfg.get('content_types', {}).keys() ) ) raise exc.HTTPNotFound # get a sorted list of hooks, by priority state.hooks = self.determine_hooks(controller) # handle "before" hooks self.handle_hooks('before', state) # fetch any parameters params = dict(request.params) # fetch the arguments for the controller args, kwargs = self.get_args( params, remainder, cfg['argspec'], im_self ) # get the result from the controller result = controller(*args, **kwargs) # a controller can return the response object which means they've taken # care of filling it out if result == response: return raw_namespace = result # pull the template out based upon content type and handle overrides template = cfg.get('content_types', {}).get( request.pecan['content_type'] ) # check if for controller override of template template = request.pecan.get('override_template', template) request.pecan['content_type'] = request.pecan.get( 'override_content_type', request.pecan['content_type'] ) # if there is a template, render it if template: if template == 'json': request.pecan['content_type'] = 'application/json' result = self.render(template, result) if 'pecan.params' in request.environ: params = request.environ.pop('pecan.params') # If we are in a test request put the namespace where it can be # accessed directly if request.environ.get('paste.testing'): testing_variables = request.environ['paste.testing_variables'] testing_variables['namespace'] = raw_namespace testing_variables['template_name'] = template testing_variables['controller_output'] = result # set the body content if isinstance(result, unicode): response.unicode_body = result else: response.body = result # set the content type if request.pecan['content_type']: response.content_type = request.pecan['content_type']
def decorate(f): expose(**kw)(f) _cfg(f)["generic_handler"] = True controller._pecan["generic_handlers"][method.upper()] = f return f
def wrap(func): cfg = _cfg(func) cfg['secured'] = Protected cfg['check_permissions'] = check_permissions_func return func
def deco(func): _cfg(func).setdefault('after_commit', []).append(action) return func
def deco(func): _cfg(func).setdefault("after_%s" % action_type, []).append(action) return func
def handle_request(self): ''' The main request handler for Pecan applications. ''' # get a sorted list of hooks, by priority (no controller hooks yet) state.hooks = self.determine_hooks() # store the routing path to allow hooks to modify it request.pecan['routing_path'] = request.path # handle "on_route" hooks self.handle_hooks('on_route', state) # lookup the controller, respecting content-type as requested # by the file extension on the URI path = request.pecan['routing_path'] if not request.pecan['content_type'] and '.' in path.split('/')[-1]: path, extension = splitext(path) request.pecan['extension'] = extension # preface with a letter to ensure compat for 2.5 request.pecan['content_type'] = guess_type('x' + extension)[0] controller, remainder = self.route(self.root, path) cfg = _cfg(controller) if cfg.get('generic_handler'): raise exc.HTTPNotFound # handle generic controllers im_self = None if cfg.get('generic'): im_self = controller.im_self handlers = cfg['generic_handlers'] controller = handlers.get(request.method, handlers['DEFAULT']) cfg = _cfg(controller) # add the controller to the state so that hooks can use it state.controller = controller # if unsure ask the controller for the default content type if not request.pecan['content_type']: request.pecan['content_type'] = cfg.get('content_type', 'text/html') elif cfg.get('content_type') is not None and \ request.pecan['content_type'] not in cfg.get('content_types', {}): print "Controller '%s' defined does not support content_type '%s'. Supported type(s): %s" % ( controller.__name__, request.pecan['content_type'], cfg.get('content_types', {}).keys() ) raise exc.HTTPNotFound # get a sorted list of hooks, by priority state.hooks = self.determine_hooks(controller) # handle "before" hooks self.handle_hooks('before', state) # fetch and validate any parameters params = dict(request.str_params) if 'schema' in cfg: params = self.validate( cfg['schema'], params, json=cfg['validate_json'], error_handler=cfg.get('error_handler'), htmlfill=cfg.get('htmlfill'), variable_decode=cfg.get('variable_decode') ) elif 'pecan.validation_errors' in request.environ: request.pecan['validation_errors'] = request.environ.pop('pecan.validation_errors') # fetch the arguments for the controller args, kwargs = self.get_args( params, remainder, cfg['argspec'], im_self ) # get the result from the controller result = controller(*args, **kwargs) # a controller can return the response object which means they've taken # care of filling it out if result == response: return raw_namespace = result # pull the template out based upon content type and handle overrides template = cfg.get('content_types', {}).get(request.pecan['content_type']) # check if for controller override of template template = request.pecan.get('override_template', template) request.pecan['content_type'] = request.pecan.get('override_content_type', request.pecan['content_type']) # if there is a template, render it if template: if template == 'json': request.pecan['content_type'] = 'application/json' result = self.render(template, result) # pass the response through htmlfill (items are popped out of the # environment even if htmlfill won't run for proper cleanup) _htmlfill = cfg.get('htmlfill') if _htmlfill is None and 'pecan.htmlfill' in request.environ: _htmlfill = request.environ.pop('pecan.htmlfill') if 'pecan.params' in request.environ: params = request.environ.pop('pecan.params') if request.pecan['validation_errors'] and _htmlfill is not None and request.pecan['content_type'] == 'text/html': errors = request.pecan['validation_errors'] result = htmlfill.render(result, defaults=params, errors=errors, text_as_default=True, **_htmlfill) # If we are in a test request put the namespace where it can be # accessed directly if request.environ.get('paste.testing'): testing_variables = request.environ['paste.testing_variables'] testing_variables['namespace'] = raw_namespace testing_variables['template_name'] = template testing_variables['controller_output'] = result # set the body content if isinstance(result, unicode): response.unicode_body = result else: response.body = result # set the content type if request.pecan['content_type']: response.content_type = request.pecan['content_type']