| | |
| | | implementation if you're still using these features. |
| | | See https://github.com/Pylons/pyramid/pull/3353 |
| | | |
| | | - Media ranges are deprecated in the ``accept`` argument of |
| | | ``pyramid.config.Configurator.add_route``. Use a list of explicit |
| | | media types to ``add_route`` to support multiple types. |
| | | |
| | | - Media ranges are deprecated in the ``accept`` argument of |
| | | ``pyramid.config.Configurator.add_view``. There is no replacement for |
| | | ranges to ``add_view``, but after much discussion the workflow is |
| | | fundamentally ambiguous in the face of various client-supplied values for |
| | | the ``Accept`` header. |
| | | See https://github.com/Pylons/pyramid/pull/3326 |
| | | |
| | | Backward Incompatibilities |
| | | -------------------------- |
| | | |
| | |
| | | ~~~~~~~~~~~~~~~~~~~~~~~ |
| | | |
| | | :app:`Pyramid` will always sort multiple views with the same ``(name, context, route_name)`` first by the specificity of the ``accept`` offer. |
| | | This means that ``text/plain`` will always be offered before ``text/*``. |
| | | Similarly ``text/plain;charset=utf8`` will always be offered before ``text/plain``. |
| | | The following order is always preserved between the following offers (more preferred to less preferred): |
| | | For any set of media type offers with the same ``type/subtype``, the offers with params will weigh more than the bare ``type/subtype`` offer. |
| | | This means that ``text/plain;charset=utf8`` will always be offered before ``text/plain``. |
| | | |
| | | - ``type/subtype;params`` |
| | | - ``type/subtype`` |
| | | - ``type/*`` |
| | | - ``*/*`` |
| | | |
| | | Within each of these levels of specificity, the ordering is ambiguous and may be controlled using :meth:`pyramid.config.Configurator.add_accept_view_order`. For example, to sort ``text/plain`` higher than ``text/html`` and to prefer a ``charset=utf8`` versus a ``charset=latin-1`` within the ``text/plain`` media type: |
| | | By default, within a given ``type/subtype``, the order of offers is ambiguous. For example, ``text/plain;charset=utf8`` versus ``text/plain;charset=latin1`` are sorted in an unspecified way. Similarly, between media types the order is also unspecified other than the defaults described below. For example, ``image/jpeg`` versus ``image/png`` versus ``application/pdf``. In these cases, the ordering may be controlled using :meth:`pyramid.config.Configurator.add_accept_view_order`. For example, to sort ``text/plain`` higher than ``text/html`` and to prefer a ``charset=utf8`` versus a ``charset=latin-1`` within the ``text/plain`` media type: |
| | | |
| | | .. code-block:: python |
| | | |
| | | config.add_accept_view_order('text/plain', weighs_more_than='text/html') |
| | | config.add_accept_view_order('text/plain;charset=utf8', weighs_more_than='text/plain;charset=latin-1') |
| | | |
| | | It is an error to try and sort accept headers across levels of specificity. You can only sort a ``type/subtype`` against another ``type/subtype``, not against a ``type/*``. That ordering is a hard requirement. |
| | | It is an error to try and sort accept headers across levels of specificity. You can only sort a ``type/subtype`` against another ``type/subtype``, not against a ``type/subtype;params``. That ordering is a hard requirement. |
| | | |
| | | By default, :app:`Pyramid` defines a very simple priority ordering for views that prefers human-readable responses over JSON: |
| | | |
| | |
| | | |
| | | from pyramid.config.util import ( |
| | | action_method, |
| | | normalize_accept_offer, |
| | | predvalseq, |
| | | ) |
| | | |
| | |
| | | |
| | | A :term:`media type` that will be matched against the ``Accept`` |
| | | HTTP request header. This value may be a specific media type such |
| | | as ``text/html``, or a range like ``text/*``, or a list of the same. |
| | | If the media type is acceptable by the ``Accept`` header of the |
| | | request, or if the ``Accept`` header isn't set at all in the |
| | | request, this predicate will match. If this does not match the |
| | | ``Accept`` header of the request, route matching continues. |
| | | as ``text/html``, or a list of the same. If the media type is |
| | | acceptable by the ``Accept`` header of the request, or if the |
| | | ``Accept`` header isn't set at all in the request, this predicate |
| | | will match. If this does not match the ``Accept`` header of the |
| | | request, route matching continues. |
| | | |
| | | If ``accept`` is not specified, the ``HTTP_ACCEPT`` HTTP header is |
| | | not taken into consideration when deciding whether or not to select |
| | | the route. |
| | | |
| | | .. versionchanged:: 1.10 |
| | | |
| | | Specifying a media range is deprecated due to changes in WebOb |
| | | and ambiguities that occur when trying to match ranges against |
| | | ranges in the ``Accept`` header. Support will be removed in |
| | | :app:`Pyramid` 2.0. Use a list of specific media types to match |
| | | more than one type. |
| | | |
| | | effective_principals |
| | | |
| | |
| | | |
| | | if accept is not None: |
| | | if not is_nonstr_iter(accept): |
| | | accept = [accept] |
| | | if '*' in accept: |
| | | warnings.warn( |
| | | ('Passing a media range to the "accept" argument of ' |
| | | 'Configurator.add_route is deprecated as of Pyramid ' |
| | | '1.10. Use a list of explicit media types.'), |
| | | DeprecationWarning, |
| | | stacklevel=3, |
| | | ) |
| | | # XXX switch this to verify=True when range support is dropped |
| | | accept = [normalize_accept_offer(accept, verify=False)] |
| | | |
| | | accept = [accept_option.lower() for accept_option in accept] |
| | | else: |
| | | accept = [ |
| | | normalize_accept_offer(accept_option) |
| | | for accept_option in accept |
| | | ] |
| | | |
| | | # these are route predicates; if they do not match, the next route |
| | | # in the routelist will be tried |
| | |
| | | return order, preds, phash.hexdigest() |
| | | |
| | | |
| | | def normalize_accept_offer(offer, verify=True): |
| | | if verify: |
| | | Accept.parse_offer(offer) |
| | | return offer.lower() |
| | | |
| | | |
| | | def sort_accept_offers(offers, order=None): |
| | | """ |
| | | Sort a list of offers by specificity and preference. |
| | | Sort a list of offers by preference. |
| | | |
| | | Supported offers are of the following forms, ordered by specificity |
| | | (higher to lower): |
| | | |
| | | - ``type/subtype;params`` and ``type/subtype`` |
| | | - ``type/*`` |
| | | - ``*/*`` |
| | | For a given ``type/subtype`` category of offers, this algorithm will |
| | | always sort offers with params higher than the bare offer. |
| | | |
| | | :param offers: A list of offers to be sorted. |
| | | :param order: A weighted list of offers where items closer to the start of |
| | |
| | | |
| | | def offer_sort_key(value): |
| | | """ |
| | | (category, type_weight, params_weight) |
| | | |
| | | category: |
| | | 1 - foo/bar and foo/bar;params |
| | | 2 - foo/* |
| | | 3 - */* |
| | | (type_weight, params_weight) |
| | | |
| | | type_weight: |
| | | if category 1 & 2: |
| | | - index of type/* in order list |
| | | - ``max_weight`` if no match is found |
| | | |
| | | - index of type/subtype in order list |
| | | - index of type/* in order list + ``max_weight`` |
| | | - index of specific ``type/subtype`` in order list |
| | | - ``max_weight * 2`` if no match is found |
| | | |
| | | params_weight: |
| | |
| | | """ |
| | | parsed = Accept.parse_offer(value) |
| | | |
| | | if value == '*/*': |
| | | return (3, 0, 0) |
| | | |
| | | elif parsed.subtype == '*': |
| | | type_w = find_order_index(value, max_weight) |
| | | return (2, type_w, 0) |
| | | |
| | | type_w = find_order_index(parsed.type + '/' + parsed.subtype, None) |
| | | if type_w is None: |
| | | type_w = max_weight + find_order_index( |
| | | parsed.type + '/*', max_weight) |
| | | type_w = find_order_index( |
| | | parsed.type + '/' + parsed.subtype, |
| | | max_weight, |
| | | ) |
| | | |
| | | if parsed.params: |
| | | param_w = find_order_index(value, max_weight) |
| | |
| | | else: |
| | | param_w = max_weight + 1 |
| | | |
| | | return (1, type_w, param_w) |
| | | return (type_w, param_w) |
| | | |
| | | return sorted(offers, key=offer_sort_key) |
| | |
| | | action_method, |
| | | DEFAULT_PHASH, |
| | | MAX_ORDER, |
| | | normalize_accept_offer, |
| | | predvalseq, |
| | | sort_accept_offers, |
| | | ) |
| | |
| | | self.views[i] = (order, view, phash) |
| | | return |
| | | |
| | | if accept is None: |
| | | if accept is None or '*' in accept: |
| | | self.views.append((order, view, phash)) |
| | | self.views.sort(key=operator.itemgetter(0)) |
| | | else: |
| | |
| | | accept |
| | | |
| | | A :term:`media type` that will be matched against the ``Accept`` |
| | | HTTP request header. This value may be a specific media type such |
| | | as ``text/html``, or a range like ``text/*``. If the media type is |
| | | HTTP request header. This value must be a specific media type such |
| | | as ``text/html`` or ``text/html;level=1``. If the media type is |
| | | acceptable by the ``Accept`` header of the request, or if the |
| | | ``Accept`` header isn't set at all in the request, this predicate |
| | | will match. If this does not match the ``Accept`` header of the |
| | |
| | | the associated view callable. |
| | | |
| | | See :ref:`accept_content_negotiation` for more information. |
| | | |
| | | .. versionchanged:: 1.10 |
| | | |
| | | Specifying a media range is deprecated and will be removed in |
| | | :app:`Pyramid` 2.0. Use explicit media types to avoid any |
| | | ambiguities in content negotiation. |
| | | |
| | | path_info |
| | | |
| | |
| | | raise ConfigurationError( |
| | | 'A list is not supported in the "accept" view predicate.', |
| | | ) |
| | | accept = accept.lower() |
| | | if '*' in accept: |
| | | warnings.warn( |
| | | ('Passing a media range to the "accept" argument of ' |
| | | 'Configurator.add_view is deprecated as of Pyramid 1.10. ' |
| | | 'Use explicit media types to avoid ambiguities in ' |
| | | 'content negotiation that may impact your users.'), |
| | | DeprecationWarning, |
| | | stacklevel=4, |
| | | ) |
| | | # XXX when media ranges are gone, switch verify=True |
| | | accept = normalize_accept_offer(accept, verify=False) |
| | | |
| | | view = self.maybe_dotted(view) |
| | | context = self.maybe_dotted(context) |
| | |
| | | .. versionadded:: 1.10 |
| | | |
| | | """ |
| | | if value == '*/*': |
| | | raise ConfigurationError( |
| | | 'cannot specify an ordering for an offer of */*') |
| | | |
| | | def normalize_type(type): |
| | | return type.lower() |
| | | |
| | | def check_type(than): |
| | | than_type, than_subtype, than_params = Accept.parse_offer(than) |
| | | if ( |
| | | # text/* vs text/plain |
| | | (offer_subtype == '*') ^ (than_subtype == '*') |
| | | # text/plain vs text/html;charset=utf8 |
| | | or (bool(offer_params) ^ bool(than_params)) |
| | | ): |
| | | # text/plain vs text/html;charset=utf8 |
| | | if bool(offer_params) ^ bool(than_params): |
| | | raise ConfigurationError( |
| | | 'cannot compare across media range specificity levels') |
| | | 'cannot compare a media type with params to one without ' |
| | | 'params') |
| | | # text/plain;charset=utf8 vs text/html;charset=utf8 |
| | | if offer_params and ( |
| | | offer_subtype != than_subtype or offer_type != than_type |
| | | ): |
| | | raise ConfigurationError( |
| | | 'cannot compare params across media types') |
| | | 'cannot compare params across different media types') |
| | | |
| | | value = normalize_type(value) |
| | | def normalize_types(thans): |
| | | thans = [normalize_accept_offer(o, verify=False) for o in thans] |
| | | for o in thans: |
| | | check_type(o) |
| | | return thans |
| | | |
| | | value = normalize_accept_offer(value, verify=False) |
| | | offer_type, offer_subtype, offer_params = Accept.parse_offer(value) |
| | | |
| | | if weighs_more_than: |
| | | if not is_nonstr_iter(weighs_more_than): |
| | | weighs_more_than = [weighs_more_than] |
| | | weighs_more_than = [normalize_type(w) for w in weighs_more_than] |
| | | for than in weighs_more_than: |
| | | check_type(than) |
| | | weighs_more_than = normalize_types(weighs_more_than) |
| | | |
| | | if weighs_less_than: |
| | | if not is_nonstr_iter(weighs_less_than): |
| | | weighs_less_than = [weighs_less_than] |
| | | weighs_less_than = [normalize_type(w) for w in weighs_less_than] |
| | | for than in weighs_less_than: |
| | | check_type(than) |
| | | weighs_less_than = normalize_types(weighs_less_than) |
| | | |
| | | discriminator = ('accept view order', value) |
| | | intr = self.introspectable( |
| | |
| | | return self.val.match(val) is not None |
| | | |
| | | class AcceptPredicate(object): |
| | | def __init__(self, val, config): |
| | | if not is_nonstr_iter(val): |
| | | val = (val,) |
| | | self.values = val |
| | | _is_using_deprecated_ranges = False |
| | | |
| | | def __init__(self, values, config): |
| | | if not is_nonstr_iter(values): |
| | | values = (values,) |
| | | # deprecated media ranges were only supported in versions of the |
| | | # predicate that didn't support lists, so check it here |
| | | if len(values) == 1 and '*' in values[0]: |
| | | self._is_using_deprecated_ranges = True |
| | | self.values = values |
| | | |
| | | def text(self): |
| | | return 'accept = %s' % (', '.join(self.values),) |
| | |
| | | phash = text |
| | | |
| | | def __call__(self, context, request): |
| | | if self._is_using_deprecated_ranges: |
| | | return self.values[0] in request.accept |
| | | return bool(request.accept.acceptable_offers(self.values)) |
| | | |
| | | class ContainmentPredicate(object): |
| | |
| | | request.accept = DummyAccept('text/html') |
| | | self.assertEqual(predicate(None, request), False) |
| | | |
| | | def test_add_route_with_wildcard_accept(self): |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_route('name', 'path', accept='text/*') |
| | | route = self._assertRoute(config, 'name', 'path', 1) |
| | | predicate = route.predicates[0] |
| | | request = self._makeRequest(config) |
| | | request.accept = DummyAccept('text/xml', contains=True) |
| | | self.assertEqual(predicate(None, request), True) |
| | | request = self._makeRequest(config) |
| | | request.accept = DummyAccept('application/json', contains=False) |
| | | self.assertEqual(predicate(None, request), False) |
| | | |
| | | def test_add_route_no_pattern_with_path(self): |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_route('name', path='path') |
| | |
| | | self.cookies = {} |
| | | |
| | | class DummyAccept(object): |
| | | def __init__(self, *matches): |
| | | def __init__(self, *matches, **kw): |
| | | self.matches = list(matches) |
| | | self.contains = kw.pop('contains', False) |
| | | |
| | | def acceptable_offers(self, offers): |
| | | results = [] |
| | |
| | | if match in offers: |
| | | results.append((match, 1.0)) |
| | | return results |
| | | |
| | | def __contains__(self, value): |
| | | return self.contains |
| | |
| | | return sort_accept_offers(offers, order) |
| | | |
| | | def test_default_specificities(self): |
| | | result = self._callFUT(['*/*', 'text/*', 'text/html', 'text/html;charset=utf8']) |
| | | result = self._callFUT(['text/html', 'text/html;charset=utf8']) |
| | | self.assertEqual(result, [ |
| | | 'text/html;charset=utf8', 'text/html', 'text/*', '*/*', |
| | | 'text/html;charset=utf8', 'text/html', |
| | | ]) |
| | | |
| | | def test_wildcard_type_order(self): |
| | | result = self._callFUT( |
| | | ['*/*', 'text/*', 'image/*'], |
| | | ['image/*', 'text/*'], |
| | | ) |
| | | self.assertEqual(result, ['image/*', 'text/*', '*/*']) |
| | | |
| | | def test_specific_type_order(self): |
| | | result = self._callFUT( |
| | |
| | | ['text/plain', 'text/html'], |
| | | ) |
| | | self.assertEqual(result, ['text/plain;charset=latin1', 'text/html;charset=utf8']) |
| | | |
| | | def test_params_inherit_wildcard_prefs(self): |
| | | result = self._callFUT( |
| | | ['image/png;progressive=1', 'text/html;charset=utf8'], |
| | | ['text/*', 'image/*'], |
| | | ) |
| | | self.assertEqual(result, ['text/html;charset=utf8', 'image/png;progressive=1']) |
| | | |
| | | def test_type_overrides_wildcard_prefs(self): |
| | | result = self._callFUT( |
| | | ['text/html;charset=utf8', 'image/png', 'foo/bar', 'text/bar'], |
| | | ['foo/*', 'text/*', 'image/*', 'image/png', 'text/html'], |
| | | ) |
| | | self.assertEqual(result, [ |
| | | 'image/png', 'text/html;charset=utf8', 'foo/bar', 'text/bar', |
| | | ]) |
| | | |
| | | class DummyCustomPredicate(object): |
| | | def __init__(self): |
| | |
| | | request.accept = DummyAccept('text/html') |
| | | self._assertNotFound(wrapper, None, request) |
| | | |
| | | def test_add_view_with_range_accept_match(self): |
| | | from pyramid.renderers import null_renderer |
| | | view = lambda *arg: 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_view(view=view, accept='text/*', renderer=null_renderer) |
| | | wrapper = self._getViewCallable(config) |
| | | request = self._makeRequest(config) |
| | | request.accept = DummyAccept('text/html', contains=True) |
| | | self.assertEqual(wrapper(None, request), 'OK') |
| | | |
| | | def test_add_view_with_range_accept_nomatch(self): |
| | | view = lambda *arg: 'OK' |
| | | config = self._makeOne(autocommit=True) |
| | | config.add_view(view=view, accept='text/*') |
| | | wrapper = self._getViewCallable(config) |
| | | request = self._makeRequest(config) |
| | | request.accept = DummyAccept('application/json', contains=False) |
| | | self._assertNotFound(wrapper, None, request) |
| | | |
| | | def test_add_view_with_containment_true(self): |
| | | from pyramid.renderers import null_renderer |
| | | from zope.interface import directlyProvides |
| | |
| | | ]) |
| | | |
| | | def test_add_accept_view_order_throws_on_wildcard(self): |
| | | from pyramid.exceptions import ConfigurationError |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises( |
| | | ConfigurationError, config.add_accept_view_order, '*/*', |
| | | ValueError, config.add_accept_view_order, '*/*', |
| | | ) |
| | | |
| | | def test_add_accept_view_order_throws_on_type_mismatch(self): |
| | | config = self._makeOne(autocommit=True) |
| | | self.assertRaises( |
| | | ConfigurationError, config.add_accept_view_order, |
| | | ValueError, config.add_accept_view_order, |
| | | 'text/*', weighs_more_than='text/html', |
| | | ) |
| | | self.assertRaises( |
| | | ConfigurationError, config.add_accept_view_order, |
| | | ValueError, config.add_accept_view_order, |
| | | 'text/html', weighs_less_than='application/*', |
| | | ) |
| | | self.assertRaises( |
| | |
| | | self.assertEqual(set(mv.accepts), set(['text/xml', 'text/html'])) |
| | | self.assertEqual(mv.views, [(99, 'view2', None), (100, 'view', None)]) |
| | | mv.add('view6', 98, accept='text/*') |
| | | self.assertEqual(mv.media_views['text/*'], [(98, 'view6', None)]) |
| | | self.assertEqual(mv.views, [ |
| | | (98, 'view6', None), (99, 'view2', None), (100, 'view', None)]) |
| | | |
| | | def test_add_with_phash(self): |
| | | mv = self._makeOne() |
| | |
| | | pass |
| | | |
| | | class DummyAccept(object): |
| | | def __init__(self, *matches): |
| | | def __init__(self, *matches, **kw): |
| | | self.matches = list(matches) |
| | | self.contains = kw.pop('contains', False) |
| | | |
| | | def acceptable_offers(self, offers): |
| | | results = [] |
| | |
| | | results.append((match, 1.0)) |
| | | return results |
| | | |
| | | def __contains__(self, value): |
| | | return self.contains |
| | | |
| | | class DummyConfig: |
| | | def __init__(self): |
| | | self.registry = DummyRegistry() |
| | |
| | | res = app.get('/hello', headers={'Accept': 'something/else'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/x-fallback') |
| | | |
| | | class AddViewAcceptArgMediaRangeAllTest(unittest.TestCase): |
| | | def setUp(self): |
| | | def view(request): |
| | | return 'text/plain' |
| | | from pyramid.config import Configurator |
| | | config = Configurator() |
| | | config.add_route('root', '/') |
| | | config.add_view( |
| | | view, route_name='root', accept='*/*', renderer='string', |
| | | ) |
| | | app = config.make_wsgi_app() |
| | | self.testapp = TestApp(app) |
| | | |
| | | def tearDown(self): |
| | | import pyramid.config |
| | | pyramid.config.global_registries.empty() |
| | | |
| | | def test_no_header(self): |
| | | res = self.testapp.get('/', headers={}, status=200) |
| | | def test_deprecated_ranges_in_route_predicate(self): |
| | | config = self._makeConfig() |
| | | config.add_route('foo', '/foo', accept='text/*') |
| | | config.add_view(lambda r: 'OK', route_name='foo', renderer='string') |
| | | app = self._makeTestApp(config) |
| | | res = app.get('/foo', headers={ |
| | | 'Accept': 'application/json; q=1.0, text/plain; q=0.9', |
| | | }, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | self.assertEqual(res.body, b'OK') |
| | | res = app.get('/foo', headers={ |
| | | 'Accept': 'application/json', |
| | | }, status=404) |
| | | self.assertEqual(res.content_type, 'application/json') |
| | | |
| | | def test_header_all(self): |
| | | res = self.testapp.get('/', headers={'Accept': '*/*'}, status=200) |
| | | def test_deprecated_ranges_in_view_predicate(self): |
| | | config = self._makeConfig() |
| | | config.add_route('foo', '/foo') |
| | | config.add_view(lambda r: 'OK', route_name='foo', |
| | | accept='text/*', renderer='string') |
| | | app = self._makeTestApp(config) |
| | | res = app.get('/foo', headers={ |
| | | 'Accept': 'application/json; q=1.0, text/plain; q=0.9', |
| | | }, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | self.assertEqual(res.body, b'OK') |
| | | res = app.get('/foo', headers={ |
| | | 'Accept': 'application/json', |
| | | }, status=404) |
| | | self.assertEqual(res.content_type, 'application/json') |
| | | |
| | | def test_header_all_subtypes_of_type(self): |
| | | res = self.testapp.get('/', headers={'Accept': 'text/*'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_specific_media_type(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_specific_media_type_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain;q=0, */*'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_type_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_all_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': '*/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | class AddViewAcceptArgMediaRangeAllSubtypesOfTypeTest(unittest.TestCase): |
| | | def setUp(self): |
| | | def view(request): |
| | | return 'text/plain' |
| | | from pyramid.config import Configurator |
| | | config = Configurator() |
| | | config.add_route('root', '/') |
| | | config.add_view( |
| | | view, route_name='root', accept='text/*', renderer='string', |
| | | ) |
| | | app = config.make_wsgi_app() |
| | | self.testapp = TestApp(app) |
| | | |
| | | def tearDown(self): |
| | | import pyramid.config |
| | | pyramid.config.global_registries.empty() |
| | | |
| | | def test_no_header(self): |
| | | res = self.testapp.get('/', headers={}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_all(self): |
| | | res = self.testapp.get('/', headers={'Accept': '*/*'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_all_subtypes_of_type(self): |
| | | res = self.testapp.get('/', headers={'Accept': 'text/*'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_specific_media_type(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_none_acceptable(self): |
| | | self.testapp.get('/', headers={'Accept': 'application/*'}, status=404) |
| | | |
| | | def test_header_ruled_out_by_specific_media_type_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain;q=0, */*'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_type_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_all_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': '*/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | class AddRouteAcceptArgMediaRangeAllTest(unittest.TestCase): |
| | | def setUp(self): |
| | | def view(request): |
| | | return 'text/plain' |
| | | from pyramid.config import Configurator |
| | | config = Configurator() |
| | | config.add_route('root', '/', accept='*/*') |
| | | config.add_view(view, route_name='root', renderer='string') |
| | | app = config.make_wsgi_app() |
| | | self.testapp = TestApp(app) |
| | | |
| | | def tearDown(self): |
| | | import pyramid.config |
| | | pyramid.config.global_registries.empty() |
| | | |
| | | def test_no_header(self): |
| | | res = self.testapp.get('/', headers={}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_all(self): |
| | | res = self.testapp.get('/', headers={'Accept': '*/*'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_all_subtypes_of_type(self): |
| | | res = self.testapp.get('/', headers={'Accept': 'text/*'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_specific_media_type(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_specific_media_type_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain;q=0, */*'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_type_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_all_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': '*/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | class AddRouteAcceptArgMediaRangeAllSubtypesOfTypeTest(unittest.TestCase): |
| | | def setUp(self): |
| | | def view(request): |
| | | return 'text/plain' |
| | | from pyramid.config import Configurator |
| | | config = Configurator() |
| | | config.add_route('root', '/', accept='text/*') |
| | | config.add_view(view, route_name='root', renderer='string') |
| | | app = config.make_wsgi_app() |
| | | self.testapp = TestApp(app) |
| | | |
| | | def tearDown(self): |
| | | import pyramid.config |
| | | pyramid.config.global_registries.empty() |
| | | |
| | | def test_no_header(self): |
| | | res = self.testapp.get('/', headers={}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_all(self): |
| | | res = self.testapp.get('/', headers={'Accept': '*/*'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_all_subtypes_of_type(self): |
| | | res = self.testapp.get('/', headers={'Accept': 'text/*'}, status=200) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_specific_media_type(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_none_acceptable(self): |
| | | self.testapp.get('/', headers={'Accept': 'application/*'}, status=404) |
| | | |
| | | def test_header_ruled_out_by_specific_media_type_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/plain;q=0, */*'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_type_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': 'text/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | def test_header_ruled_out_by_all_range_q0(self): |
| | | res = self.testapp.get( |
| | | '/', headers={'Accept': '*/*;q=0, text/html'}, status=200, |
| | | ) |
| | | self.assertEqual(res.content_type, 'text/plain') |
| | | |
| | | class DummyContext(object): |
| | | pass |