Merge branch 'master' into 1.3-branch
| | |
| | | http://readthedocs.org/docs/venusian/en/latest/#ignore-scan-argument for |
| | | more information about how to use the ``ignore`` argument to ``scan``. |
| | | |
| | | - Better error messages when a view callable returns a value that cannot be |
| | | converted to a response (for example, when a view callable returns a |
| | | dictionary without a renderer defined, or doesn't return any value at all). |
| | | The error message now contains information about the view callable itself |
| | | as well as the result of calling it. |
| | | |
| | | Dependencies |
| | | ------------ |
| | | |
| | |
| | | Using such wrappers, we strive to always hide the ZCA API from application |
| | | developers. Application developers should just never know about the ZCA API: |
| | | they should call a Python function with some object germane to the domain as |
| | | an argument, and it should returns a result. A corollary that follows is |
| | | an argument, and it should return a result. A corollary that follows is |
| | | that any reader of an application that has been written using :app:`Pyramid` |
| | | needn't understand the ZCA API either. |
| | | |
| | |
| | | The :mod:`zope.component`, package on which :app:`Pyramid` depends has |
| | | transitive dependencies on several other packages (:mod:`zope.event`, and |
| | | :mod:`zope.interface`). :app:`Pyramid` also has its own direct dependencies, |
| | | such as :term:`PasteDeploy`, :term:`Chameleon`, :term:`Mako` :term:`WebOb`, |
| | | such as :term:`PasteDeploy`, :term:`Chameleon`, :term:`Mako`, :term:`WebOb`, |
| | | :mod:`zope.deprecation` and some of these in turn have their own transitive |
| | | dependencies. |
| | | |
| | |
| | | |
| | | View callables are, at the risk of sounding obvious, callable Python |
| | | objects. Specifically, view callables can be functions, classes, or instances |
| | | that implement an ``__call__`` method (making the instance callable). |
| | | that implement a ``__call__`` method (making the instance callable). |
| | | |
| | | View callables must, at a minimum, accept a single argument named |
| | | ``request``. This argument represents a :app:`Pyramid` :term:`Request` |
| | |
| | | from pyramid.interfaces import IActionInfo |
| | | |
| | | from pyramid.compat import ( |
| | | string_types, |
| | | bytes_, |
| | | is_nonstr_iter, |
| | | ) |
| | |
| | | self._ainfo = [] |
| | | info = kw.pop('_info', None) |
| | | # backframes for outer decorators to actionmethods |
| | | backframes = kw.pop('_backframes', 2) |
| | | backframes = kw.pop('_backframes', 2) |
| | | if is_nonstr_iter(info) and len(info) == 4: |
| | | # _info permitted as extract_stack tuple |
| | | info = ActionInfo(*info) |
| | |
| | | request_method = sorted(request_method) |
| | | def request_method_predicate(context, request): |
| | | return request.method in request_method |
| | | text = "request method = %s" % repr(request_method) |
| | | text = "request method = %r" % request_method |
| | | request_method_predicate.__text__ = text |
| | | weights.append(1 << 2) |
| | | predicates.append(request_method_predicate) |
| | |
| | | from pyramid.static import static_view |
| | | from pyramid.threadlocal import get_current_registry |
| | | from pyramid.view import render_view_to_response |
| | | from pyramid.util import object_description |
| | | |
| | | from pyramid.config.util import ( |
| | | DEFAULT_PHASH, |
| | |
| | | |
| | | urljoin = urlparse.urljoin |
| | | url_parse = urlparse.urlparse |
| | | |
| | | def view_description(view): |
| | | try: |
| | | return view.__text__ |
| | | except AttributeError: |
| | | return object_description(view) |
| | | |
| | | def wraps_view(wrapper): |
| | | def inner(self, view): |
| | |
| | | # "wrapped view" |
| | | for attr in ('__permitted__', '__call_permissive__', '__permission__', |
| | | '__predicated__', '__predicates__', '__accept__', |
| | | '__order__'): |
| | | '__order__', '__text__'): |
| | | try: |
| | | setattr(wrapper, attr, getattr(view, attr)) |
| | | except AttributeError: |
| | |
| | | result = view(context, request) |
| | | response = registry.queryAdapterOrSelf(result, IResponse) |
| | | if response is None: |
| | | raise ValueError( |
| | | 'Could not convert view return value "%s" into a ' |
| | | 'response object' % (result,)) |
| | | if result is None: |
| | | append = (' You may have forgotten to return a value from ' |
| | | 'the view callable.') |
| | | elif isinstance(result, dict): |
| | | append = (' You may have forgotten to define a renderer in ' |
| | | 'the view configuration.') |
| | | else: |
| | | append = '' |
| | | msg = ('Could not convert return value of the view callable %s ' |
| | | 'into a response object. ' |
| | | 'The value returned was %r.' + append) |
| | | |
| | | raise ValueError(msg % (view_description(view), result)) |
| | | return response |
| | | |
| | | return viewresult_to_response |
| | |
| | | mapped_view = self.map_class_requestonly(view) |
| | | else: |
| | | mapped_view = self.map_class_native(view) |
| | | mapped_view.__text__ = 'method %s of %s' % ( |
| | | self.attr or '__call__', object_description(view)) |
| | | return mapped_view |
| | | |
| | | def map_nonclass(self, view): |
| | |
| | | mapped_view = self.map_nonclass_requestonly(view) |
| | | elif self.attr: |
| | | mapped_view = self.map_nonclass_attr(view) |
| | | if self.attr is not None: |
| | | mapped_view.__text__ = 'attr %s of %s' % ( |
| | | self.attr, object_description(view)) |
| | | else: |
| | | mapped_view.__text__ = object_description(view) |
| | | return mapped_view |
| | | |
| | | def map_class_requestonly(self, view): |
| | |
| | | self.config.registry.registerUtility(policy, IAuthenticationPolicy) |
| | | self.config.registry.registerUtility(policy, IAuthorizationPolicy) |
| | | |
| | | def test_function_returns_non_adaptable(self): |
| | | def view(request): |
| | | return None |
| | | deriver = self._makeOne() |
| | | result = deriver(view) |
| | | self.assertFalse(result is view) |
| | | try: |
| | | result(None, None) |
| | | except ValueError as e: |
| | | self.assertEqual( |
| | | e.args[0], |
| | | 'Could not convert return value of the view callable function ' |
| | | 'pyramid.tests.test_config.test_views.view into a response ' |
| | | 'object. The value returned was None. You may have forgotten ' |
| | | 'to return a value from the view callable.' |
| | | ) |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_function_returns_non_adaptable_dict(self): |
| | | def view(request): |
| | | return {'a':1} |
| | | deriver = self._makeOne() |
| | | result = deriver(view) |
| | | self.assertFalse(result is view) |
| | | try: |
| | | result(None, None) |
| | | except ValueError as e: |
| | | self.assertEqual( |
| | | e.args[0], |
| | | "Could not convert return value of the view callable function " |
| | | "pyramid.tests.test_config.test_views.view into a response " |
| | | "object. The value returned was {'a': 1}. You may have " |
| | | "forgotten to define a renderer in the view configuration." |
| | | ) |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_instance_returns_non_adaptable(self): |
| | | class AView(object): |
| | | def __call__(self, request): |
| | | return None |
| | | view = AView() |
| | | deriver = self._makeOne() |
| | | result = deriver(view) |
| | | self.assertFalse(result is view) |
| | | try: |
| | | result(None, None) |
| | | except ValueError as e: |
| | | msg = e.args[0] |
| | | self.assertTrue(msg.startswith( |
| | | 'Could not convert return value of the view callable object ' |
| | | '<pyramid.tests.test_config.test_views.AView object at')) |
| | | self.assertTrue(msg.endswith( |
| | | '> into a response object. The value returned was None. You ' |
| | | 'may have forgotten to return a value from the view callable.')) |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_requestonly_default_method_returns_non_adaptable(self): |
| | | request = DummyRequest() |
| | | class AView(object): |
| | | def __init__(self, request): |
| | | pass |
| | | def __call__(self): |
| | | return None |
| | | deriver = self._makeOne() |
| | | result = deriver(AView) |
| | | self.assertFalse(result is AView) |
| | | try: |
| | | result(None, request) |
| | | except ValueError as e: |
| | | self.assertEqual( |
| | | e.args[0], |
| | | 'Could not convert return value of the view callable ' |
| | | 'method __call__ of ' |
| | | 'class pyramid.tests.test_config.test_views.AView into a ' |
| | | 'response object. The value returned was None. You may have ' |
| | | 'forgotten to return a value from the view callable.' |
| | | ) |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_requestonly_nondefault_method_returns_non_adaptable(self): |
| | | request = DummyRequest() |
| | | class AView(object): |
| | | def __init__(self, request): |
| | | pass |
| | | def theviewmethod(self): |
| | | return None |
| | | deriver = self._makeOne(attr='theviewmethod') |
| | | result = deriver(AView) |
| | | self.assertFalse(result is AView) |
| | | try: |
| | | result(None, request) |
| | | except ValueError as e: |
| | | self.assertEqual( |
| | | e.args[0], |
| | | 'Could not convert return value of the view callable ' |
| | | 'method theviewmethod of ' |
| | | 'class pyramid.tests.test_config.test_views.AView into a ' |
| | | 'response object. The value returned was None. You may have ' |
| | | 'forgotten to return a value from the view callable.' |
| | | ) |
| | | else: # pragma: no cover |
| | | raise AssertionError |
| | | |
| | | def test_requestonly_function(self): |
| | | response = DummyResponse() |
| | | def view(request): |
| | |
| | | view_attr='attr') |
| | | self.assertEqual(config.view_kw['attr'], 'attr') |
| | | |
| | | class Test_view_description(unittest.TestCase): |
| | | def _callFUT(self, view): |
| | | from pyramid.config.views import view_description |
| | | return view_description(view) |
| | | |
| | | def test_with_text(self): |
| | | def view(): pass |
| | | view.__text__ = 'some text' |
| | | result = self._callFUT(view) |
| | | self.assertEqual(result, 'some text') |
| | | |
| | | def test_without_text(self): |
| | | def view(): pass |
| | | result = self._callFUT(view) |
| | | self.assertEqual(result, |
| | | 'function pyramid.tests.test_config.test_views.view') |
| | | |
| | | |
| | | class DummyRegistry: |
| | | pass |
| | | |
| | |
| | | string_types, |
| | | binary_type, |
| | | is_nonstr_iter, |
| | | decode_path_info, |
| | | ) |
| | | |
| | | from pyramid.exceptions import URLDecodeError |
| | | |
| | | from pyramid.traversal import ( |
| | | quote_path_segment, |
| | | decode_path_info, |
| | | split_path_info, |
| | | ) |
| | | |