Пример #1
0
    def lookup_template_engine(self, tgl):
        """Return the template engine data.

        Provides a convenience method to get the proper engine,
        content_type, template, and exclude_names for a particular
        tg_format (which is pulled off of the request headers).

        """
        request = tgl.request
        response = tgl.response

        try:
            render_custom_format = request._render_custom_format[
                self.controller]
        except:
            render_custom_format = self.render_custom_format

        if render_custom_format:
            (content_type, engine, template, exclude_names,
             render_params) = self.custom_engines[render_custom_format]
        else:
            if self.default_engine:
                content_type = self.default_engine
            elif self.engines:
                if response.content_type is not None:
                    # Check for overridden content type from the controller call
                    accept_types = create_accept_header(response.content_type)
                elif request._response_type and request._response_type in self.engines:
                    # Check for content type detected by request extensions
                    accept_types = create_accept_header(request._response_type)
                else:
                    accept_types = request.accept

                best_matches = (
                    accept_types.acceptable_offers(self.engines_keys) or
                    # If none of the available engines matches with the
                    # available options, just suggest usage of the first engine.
                    (
                        (self.engines_keys[0], None), ))
                content_type = best_matches[0][0]
            else:
                content_type = 'text/html'

            # check for overridden templates
            try:
                cnt_override_mapping = request._override_mapping[
                    self.controller]
                engine, template, exclude_names, render_params = cnt_override_mapping[
                    content_type.split(";")[0]]
            except (AttributeError, KeyError):
                (engine, template, exclude_names,
                 render_params) = self.engines.get(content_type, (None, ) * 4)

        return content_type, engine, template, exclude_names, render_params
Пример #2
0
    def lookup_template_engine(self, tgl):
        """Return the template engine data.

        Provides a convenience method to get the proper engine,
        content_type, template, and exclude_names for a particular
        tg_format (which is pulled off of the request headers).

        """
        request = tgl.request
        response = tgl.response

        try:
            render_custom_format = request._render_custom_format[self.controller]
        except:
            render_custom_format = self.render_custom_format

        if render_custom_format:
            (content_type, engine, template, exclude_names, render_params
                ) = self.custom_engines[render_custom_format]
        else:
            if self.default_engine:
                content_type = self.default_engine
            elif self.engines:
                if response.content_type is not None:
                    # Check for overridden content type from the controller call
                    accept_types = create_accept_header(response.content_type)
                elif request._response_type and request._response_type in self.engines:
                    # Check for content type detected by request extensions
                    accept_types = create_accept_header(request._response_type)
                else:
                    accept_types = request.accept

                best_matches = (
                    accept_types.acceptable_offers(self.engines_keys) or
                    # If none of the available engines matches with the
                    # available options, just suggest usage of the first engine.
                    ((self.engines_keys[0], None), )
                )
                content_type = best_matches[0][0]
            else:
                content_type = 'text/html'

            # check for overridden templates
            try:
                cnt_override_mapping = request._override_mapping[self.controller]
                engine, template, exclude_names, render_params = cnt_override_mapping[content_type.split(";")[0]]
            except (AttributeError, KeyError):
                (engine, template, exclude_names, render_params
                    ) = self.engines.get(content_type, (None,) * 4)

        return content_type, engine, template, exclude_names, render_params
Пример #3
0
    def generate_response(self, environ, start_response):
        if self.content_length is not None:
            del self.content_length
        headerlist = list(self.headerlist)
        accept_value = environ.get('HTTP_ACCEPT', '')
        accept_header = create_accept_header(header_value=accept_value)
        acceptable_offers = accept_header.acceptable_offers(
            offers=['text/html', 'application/json'],
        )
        match = acceptable_offers[0][0] if acceptable_offers else None

        if match == 'text/html':
            content_type = 'text/html'
            body = self.html_body(environ)
        elif match == 'application/json':
            content_type = 'application/json'
            body = self.json_body(environ)
        else:
            content_type = 'text/plain'
            body = self.plain_body(environ)
        resp = Response(body,
                        status=self.status,
                        headerlist=headerlist,
                        content_type=content_type,
                        )
        resp.content_type = content_type
        return resp(environ, start_response)
Пример #4
0
    def prepare(self, environ):
        if not self.body:
            accept_value = environ.get("HTTP_ACCEPT", "")
            accept = create_accept_header(accept_value)

            # Attempt to match XML or JSON, if those don't match, we will fall back to defaulting to JSON
            #   since browsers add HTML automatically and it is closer to XML, we 'allow' it only to catch this
            #   explicit case and fallback to JSON manually
            match = accept.best_match([
                CONTENT_TYPE_TEXT_HTML, CONTENT_TYPE_APP_JSON,
                CONTENT_TYPE_TEXT_XML, CONTENT_TYPE_APP_XML
            ],
                                      default_match=CONTENT_TYPE_APP_JSON)
            if match == CONTENT_TYPE_TEXT_HTML:
                match = CONTENT_TYPE_APP_JSON

            if match == CONTENT_TYPE_APP_JSON:
                self.content_type = CONTENT_TYPE_APP_JSON

                # json exception response should not have status 200
                if self.status_code == HTTPOk.code:
                    self.status = HTTPInternalServerError.code

                class JsonPageTemplate(object):
                    def __init__(self, excobj):
                        self.excobj = excobj

                    def substitute(self, code, locator, message):
                        status = self.excobj.status
                        title = getattr(self.excobj, "code", None)
                        data = self.excobj.json_formatter(status=status,
                                                          body=message,
                                                          title=title,
                                                          environ=environ)
                        data["exception"] = {
                            "code": code or "",
                            "locator": locator or "",
                            "message": message or "",
                        }
                        return json.dumps(data)

                page_template = JsonPageTemplate(self)
                args = {
                    "code": self.code,
                    "locator": self.locator,
                    "message": self.message
                }
            else:
                self.content_type = CONTENT_TYPE_TEXT_XML
                page_template = self.page_template
                args = {
                    "code": self.code,
                    "locator": self.locator,
                    "message": self.message or "",
                }
            page = page_template.substitute(**args)
            if isinstance(page, str):
                page = page.encode(self.charset if self.charset else "UTF-8")
            self.app_iter = [page]
            self.body = page
Пример #5
0
    def generate_response(self, environ, start_response):
        if self.content_length is not None:
            del self.content_length
        headerlist = list(self.headerlist)
        accept_value = environ.get("HTTP_ACCEPT", "")
        accept_header = create_accept_header(header_value=accept_value)
        acceptable_offers = accept_header.acceptable_offers(
            offers=["text/html", "application/json"]
        )
        match = acceptable_offers[0][0] if acceptable_offers else None

        if match == "text/html":
            content_type = "text/html"
            body = self.html_body(environ)
        elif match == "application/json":
            content_type = "application/json"
            body = self.json_body(environ)
        else:
            content_type = "text/plain"
            body = self.plain_body(environ)
        resp = Response(
            body, status=self.status, headerlist=headerlist, content_type=content_type
        )
        resp.content_type = content_type

        return resp(environ, start_response)
Пример #6
0
    def test_it_sets_response_header_based_on_value_of_accept(
            self, pyramid_request, testview, accept, expected_header):
        pyramid_request.accept = create_accept_header(accept)

        res = version_media_type_header(testview)(None, pyramid_request)

        assert res.headers["Hypothesis-Media-Type"] == expected_header
Пример #7
0
    def test_it_replaces_context_with_406_if_accept_set_and_invalid(
            self, pyramid_request, testview):
        # None of these is valid
        pyramid_request.accept = create_accept_header(
            "application/something+json, foo/bar")
        fake_context = mock.Mock()
        validate_media_types(testview)(fake_context, pyramid_request)

        context, _ = testview.call_args[0]
        assert isinstance(context, HTTPNotAcceptable)
Пример #8
0
    def test_it_does_not_modify_context_if_any_accept_values_ok(
            self, pyramid_request, testview):
        # At least one of these is valid
        pyramid_request.accept = create_accept_header(
            "application/json, foo/bar")
        fake_context = mock.Mock()
        validate_media_types(testview)(fake_context, pyramid_request)

        context, _ = testview.call_args[0]
        assert context == fake_context
Пример #9
0
    def prepare(self, environ):
        if not self.body:
            accept_value = environ.get("HTTP_ACCEPT", "")
            accept = create_accept_header(accept_value)

            # Attempt to match xml or json, if those don't match, we will fall through to defaulting to xml
            match = accept.best_match(
                [CONTENT_TYPE_TEXT_XML, CONTENT_TYPE_APP_JSON])

            if match == CONTENT_TYPE_APP_JSON:
                self.content_type = CONTENT_TYPE_APP_JSON

                # json exception response should not have status 200
                if self.status_code == HTTPOk.code:
                    self.status = HTTPInternalServerError.code

                class JsonPageTemplate(object):
                    def __init__(self, excobj):
                        self.excobj = excobj

                    def substitute(self, code, locator, message):
                        status = self.excobj.status
                        data = self.excobj.json_formatter(status=status,
                                                          body=message,
                                                          title=None,
                                                          environ=environ)
                        data["exception"] = {
                            "code": code or "",
                            "locator": locator or "",
                            "message": message or "",
                        }
                        return json.dumps(data)

                page_template = JsonPageTemplate(self)

            else:
                self.content_type = CONTENT_TYPE_TEXT_XML
                page_template = self.page_template

            args = {
                "code": self.code,
                "locator": self.locator,
                "message": self.message or "",
            }
            page = page_template.substitute(**args)
            if isinstance(page, str):
                page = page.encode(self.charset if self.charset else "UTF-8")
            self.app_iter = [page]
            self.body = page
Пример #10
0
 def _get_accept(self):
     if self._accept is None:
         self._accept = create_accept_header(None)
     return self._accept
Пример #11
0
 def _set_accept(self, value):
     self._accept = create_accept_header(value)
Пример #12
0
 def pyramid_request(self, pyramid_request):
     # Set an empty accept on the request, imitating what pyramid does
     # in real life if no Accept header is set on the incoming request
     pyramid_request.accept = create_accept_header(None)
     return pyramid_request
Пример #13
0
    def __call__(self, environ, start_response):
        '''
        Implements the WSGI specification for Pecan applications, utilizing
        ``WebOb``.
        '''

        # create the request and response object
        req = self.request_cls(environ)
        resp = self.response_cls()
        state = RoutingState(req, resp, self)
        environ['pecan.locals'] = {'request': req, 'response': resp}
        controller = None

        # track internal redirects
        internal_redirect = False

        # handle the request
        try:
            # add context and environment to the request
            req.context = environ.get('pecan.recursive.context', {})
            req.pecan = dict(content_type=None)

            controller, args, kwargs = self.find_controller(state)
            self.invoke_controller(controller, args, kwargs, state)
        except Exception as e:
            # if this is an HTTP Exception, set it as the response
            if isinstance(e, exc.HTTPException):
                # if the client asked for JSON, do our best to provide it
                accept_header = acceptparse.create_accept_header(
                    getattr(req.accept, 'header_value', '*/*') or '*/*')
                offers = accept_header.acceptable_offers(
                    ('text/plain', 'text/html', 'application/json'))
                best_match = offers[0][0] if offers else None
                state.response = e
                if best_match == 'application/json':
                    json_body = dumps({
                        'code': e.status_int,
                        'title': e.title,
                        'description': e.detail
                    })
                    if isinstance(json_body, six.text_type):
                        e.text = json_body
                    else:
                        e.body = json_body
                    state.response.content_type = best_match
                environ['pecan.original_exception'] = e

            # note if this is an internal redirect
            internal_redirect = isinstance(e, ForwardRequestException)

            # if this is not an internal redirect, run error hooks
            on_error_result = None
            if not internal_redirect:
                on_error_result = self.handle_hooks(
                    self.determine_hooks(state.controller), 'on_error', state,
                    e)

            # if the on_error handler returned a Response, use it.
            if isinstance(on_error_result, WebObResponse):
                state.response = on_error_result
            else:
                if not isinstance(e, exc.HTTPException):
                    raise

            # if this is an HTTP 405, attempt to specify an Allow header
            if isinstance(e, exc.HTTPMethodNotAllowed) and controller:
                allowed_methods = _cfg(controller).get('allowed_methods', [])
                if allowed_methods:
                    state.response.allow = sorted(allowed_methods)
        finally:
            # if this is not an internal redirect, run "after" hooks
            if not internal_redirect:
                self.handle_hooks(self.determine_hooks(state.controller),
                                  'after', state)

        self._handle_empty_response_body(state)

        # get the response
        return state.response(environ, start_response)
Пример #14
0
    def find_controller(self, state):
        '''
        The main request handler for Pecan applications.
        '''
        # get a sorted list of hooks, by priority (no controller hooks yet)
        req = state.request
        pecan_state = req.pecan

        # store the routing path for the current application to allow hooks to
        # modify it
        pecan_state['routing_path'] = path = req.path_info

        # handle "on_route" hooks
        self.handle_hooks(self.hooks, 'on_route', state)

        # lookup the controller, respecting content-type as requested
        # by the file extension on the URI
        pecan_state['extension'] = None

        # attempt to guess the content type based on the file extension
        if self.guess_content_type_from_ext \
                and not pecan_state['content_type'] \
                and '.' in path:
            _, extension = splitext(path.rstrip('/'))

            # preface with a letter to ensure compat for 2.5
            potential_type = guess_type('x' + extension)[0]

            if extension and potential_type is not None:
                path = ''.join(path.rsplit(extension, 1))
                pecan_state['extension'] = extension
                pecan_state['content_type'] = potential_type

        controller, remainder = self.route(req, 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 = six.get_method_self(controller)
            handlers = cfg['generic_handlers']
            controller = handlers.get(req.method, handlers['DEFAULT'])
            handle_security(controller, im_self)
            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
        content_types = cfg.get('content_types', {})
        if not pecan_state['content_type']:
            # attempt to find a best match based on accept headers (if they
            # exist)
            accept = getattr(req.accept, 'header_value', '*/*') or '*/*'
            if accept == '*/*' or (accept.startswith('text/html,') and list(
                    content_types.keys()) in self.SIMPLEST_CONTENT_TYPES):
                pecan_state['content_type'] = cfg.get('content_type',
                                                      'text/html')
            else:
                best_default = None
                accept_header = acceptparse.create_accept_header(accept)
                offers = accept_header.acceptable_offers(
                    list(content_types.keys()))
                if offers:
                    # If content type matches exactly use matched type
                    best_default = offers[0][0]
                else:
                    # If content type doesn't match exactly see if something
                    # matches when not using parameters
                    for k in content_types.keys():
                        if accept.startswith(k):
                            best_default = k
                            break

                if best_default is None:
                    msg = "Controller '%s' defined does not support " + \
                          "content_type '%s'. Supported type(s): %s"
                    logger.error(
                        msg %
                        (controller.__name__, pecan_state['content_type'],
                         content_types.keys()))
                    raise exc.HTTPNotAcceptable()

                pecan_state['content_type'] = best_default
        elif cfg.get('content_type') is not None and \
                pecan_state['content_type'] not in content_types:

            msg = "Controller '%s' defined does not support content_type " + \
                  "'%s'. Supported type(s): %s"
            logger.error(msg %
                         (controller.__name__, pecan_state['content_type'],
                          content_types.keys()))
            raise exc.HTTPNotFound

        # fetch any parameters
        if req.method == 'GET':
            params = req.GET
        elif req.content_type in ('application/json',
                                  'application/javascript'):
            try:
                if not isinstance(req.json, dict):
                    raise TypeError('%s is not a dict' % req.json)
                params = NestedMultiDict(req.GET, req.json)
            except (TypeError, ValueError):
                params = req.params
        else:
            params = req.params

        # fetch the arguments for the controller
        args, varargs, kwargs = self.get_args(state, params.mixed(), remainder,
                                              cfg['argspec'], im_self)
        state.arguments = Arguments(args, varargs, kwargs)

        # handle "before" hooks
        self.handle_hooks(self.determine_hooks(controller), 'before', state)

        return controller, args + varargs, kwargs
Пример #15
0
 def _get_accept(self):
     if self._accept is None:
         self._accept = create_accept_header(None)
     return self._accept
Пример #16
0
 def _set_accept(self, value):
     self._accept = create_accept_header(value)
Пример #17
0
    def prepare(self, environ):
        if not self.has_body and not self.empty_body:
            html_comment = ''
            comment = self.comment or ''
            accept_value = environ.get('HTTP_ACCEPT', '')
            accept = create_accept_header(accept_value)
            # Attempt to match text/html or application/json, if those don't
            # match, we will fall through to defaulting to text/plain
            acceptable = accept.acceptable_offers(['text/html', 'application/json'])
            acceptable = [offer[0] for offer in acceptable] + ['text/plain']
            match = acceptable[0]

            if match == 'text/html':
                self.content_type = 'text/html'
                escape = _html_escape
                page_template = self.html_template_obj
                br = '<br/>'
                if comment:
                    html_comment = '<!-- %s -->' % escape(comment)
            elif match == 'application/json':
                self.content_type = 'application/json'
                self.charset = None
                escape = _no_escape
                br = '\n'
                if comment:
                    html_comment = escape(comment)

                class JsonPageTemplate(object):
                    def __init__(self, excobj):
                        self.excobj = excobj

                    def substitute(self, status, body):
                        jsonbody = self.excobj._json_formatter(
                            status=status,
                            body=body, title=self.excobj.title,
                            environ=environ)
                        return json.dumps(jsonbody)

                page_template = JsonPageTemplate(self)
            else:
                self.content_type = 'text/plain'
                escape = _no_escape
                page_template = self.plain_template_obj
                br = '\n'
                if comment:
                    html_comment = escape(comment)
            args = {
                'br': br,
                'explanation': escape(self.explanation),
                'detail': escape(self.detail or ''),
                'comment': escape(comment),
                'html_comment': html_comment,
                }
            body_tmpl = self.body_template_obj
            if HTTPException.body_template_obj is not body_tmpl:
                # Custom template; add headers to args
                for k, v in environ.items():
                    if (not k.startswith('wsgi.')) and ('.' in k):
                        # omit custom environ variables, stringifying them may
                        # trigger code that should not be executed here; see
                        # https://github.com/Pylons/pyramid/issues/239
                        continue
                    args[k] = escape(v)
                for k, v in self.headers.items():
                    args[k.lower()] = escape(v)
            body = body_tmpl.substitute(args)
            page = page_template.substitute(status=self.status, body=body)
            if isinstance(page, text_type):
                page = page.encode(self.charset if self.charset else 'UTF-8')
            self.app_iter = [page]
            self.body = page
Пример #18
0
    def prepare(self, environ):
        if not self.has_body and not self.empty_body:
            html_comment = ''
            comment = self.comment or ''
            accept_value = environ.get('HTTP_ACCEPT', '')
            accept = create_accept_header(accept_value)
            # Attempt to match text/html or application/json, if those don't
            # match, we will fall through to defaulting to text/plain
            acceptable = accept.acceptable_offers(
                ['text/html', 'application/json'])
            acceptable = [offer[0] for offer in acceptable] + ['text/plain']
            match = acceptable[0]

            if match == 'text/html':
                self.content_type = 'text/html'
                escape = _html_escape
                page_template = self.html_template_obj
                br = '<br/>'
                if comment:
                    html_comment = '<!-- %s -->' % escape(comment)
            elif match == 'application/json':
                self.content_type = 'application/json'
                self.charset = None
                escape = _no_escape
                br = '\n'
                if comment:
                    html_comment = escape(comment)

                class JsonPageTemplate(object):
                    def __init__(self, excobj):
                        self.excobj = excobj

                    def substitute(self, status, body):
                        jsonbody = self.excobj._json_formatter(
                            status=status,
                            body=body,
                            title=self.excobj.title,
                            environ=environ,
                        )
                        return json.dumps(jsonbody)

                page_template = JsonPageTemplate(self)
            else:
                self.content_type = 'text/plain'
                escape = _no_escape
                page_template = self.plain_template_obj
                br = '\n'
                if comment:
                    html_comment = escape(comment)
            args = {
                'br': br,
                'explanation': escape(self.explanation),
                'detail': escape(self.detail or ''),
                'comment': escape(comment),
                'html_comment': html_comment,
            }
            body_tmpl = self.body_template_obj
            if HTTPException.body_template_obj is not body_tmpl:
                # Custom template; add headers to args
                for k, v in environ.items():
                    if (not k.startswith('wsgi.')) and ('.' in k):
                        # omit custom environ variables, stringifying them may
                        # trigger code that should not be executed here; see
                        # https://github.com/Pylons/pyramid/issues/239
                        continue
                    args[k] = escape(v)
                for k, v in self.headers.items():
                    args[k.lower()] = escape(v)
            body = body_tmpl.substitute(args)
            page = page_template.substitute(status=self.status, body=body)
            if isinstance(page, str):
                page = page.encode(self.charset if self.charset else 'UTF-8')
            self.app_iter = [page]
            self.body = page