Beispiel #1
0
 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
Beispiel #2
0
def accept_noncanonical(func):
    '''
    Flags a controller method as accepting non-canoncial URLs.
    '''
    
    _cfg(func)['accept_noncanonical'] = True
    return func
Beispiel #3
0
def accept_noncanonical(func):
    """
    Flags a controller method as accepting non-canoncial URLs.
    """

    _cfg(func)["accept_noncanonical"] = True
    return func
Beispiel #4
0
    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
Beispiel #5
0
 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
Beispiel #6
0
 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)
         )
     )
Beispiel #7
0
 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)
Beispiel #8
0
    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()
Beispiel #9
0
    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
Beispiel #10
0
    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
Beispiel #11
0
    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
Beispiel #12
0
    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()
Beispiel #13
0
def _unlocked_method(func):
    _cfg(func)['secured'] = Any
    return func
Beispiel #14
0
    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']
Beispiel #15
0
 def decorate(f):
     expose(**kw)(f)
     _cfg(f)["generic_handler"] = True
     controller._pecan["generic_handlers"][method.upper()] = f
     return f
Beispiel #16
0
 def wrap(func):
     cfg = _cfg(func)
     cfg['secured'] = Protected
     cfg['check_permissions'] = check_permissions_func
     return func
Beispiel #17
0
 def deco(func):
     _cfg(func).setdefault('after_commit', []).append(action)
     return func
Beispiel #18
0
 def deco(func):
     _cfg(func).setdefault("after_%s" % action_type, []).append(action)
     return func
Beispiel #19
0
    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']