Chris McDonough
2012-03-20 676131b34addf444f58a3dec011dd5f05329b0cf
Merge branch '1.3-branch'
4 files modified
51 ■■■■ changed files
CHANGES.txt 5 ●●●●● patch | view | raw | blame | history
pyramid/compat.py 7 ●●●●● patch | view | raw | blame | history
pyramid/config/views.py 27 ●●●●● patch | view | raw | blame | history
pyramid/tests/test_config/test_views.py 12 ●●●●● patch | view | raw | blame | history
CHANGES.txt
@@ -11,6 +11,11 @@
  because it mistakenly detects that a route was matched when, in fact, it
  was not.
- The fix for issue https://github.com/Pylons/pyramid/issues/461 (which made
  it possible for instance methods to be used as view callables) introduced a
  backwards incompatibility when methods that declared only a request
  argument were used.  See https://github.com/Pylons/pyramid/issues/503
1.3b3 (2012-03-17)
==================
pyramid/compat.py
@@ -1,3 +1,4 @@
import inspect
import platform
import sys
import types
@@ -185,8 +186,10 @@
    
if PY3: # pragma: no cover
    im_func = '__func__'
    im_self = '__self__'
else:
    im_func = 'im_func'
    im_self = 'im_self'
try: # pragma: no cover
    import configparser
@@ -237,3 +240,7 @@
    from urlparse import unquote as unquote_to_bytes
    def unquote_bytes_to_wsgi(bytestring):
        return unquote_to_bytes(bytestring)
def is_bound_method(ob):
    return inspect.ismethod(ob) and getattr(ob, im_self, None) is not None
pyramid/config/views.py
@@ -41,6 +41,7 @@
    im_func,
    url_quote,
    WIN,
    is_bound_method,
    )
from pyramid.exceptions import (
@@ -140,18 +141,7 @@
                                self.decorated_view(
                                    self.rendered_view(
                                        self.mapped_view(
                                            self.text_wrapped_view(
                                                view))))))))))
    @wraps_view
    def text_wrapped_view(self, view):
        # if the method is an instance method, we need to wrap it in order
        # to be able to assign a __text__ value to it later.  see #461.
        if inspect.ismethod(view):
            def text_wrapper(context, request):
                return view(context, request)
            return text_wrapper
        return view
                                                view)))))))))
    @wraps_view
    def mapped_view(self, view):
@@ -428,9 +418,16 @@
        elif self.attr:
            mapped_view = self.map_nonclass_attr(view)
        if inspect.isroutine(mapped_view):
            # we potentially mutate an unwrapped view here if it's a function;
            # we do this to avoid function call overhead of injecting another
            # wrapper
            # This branch will be true if the view is a function or a method.
            # We potentially mutate an unwrapped object here if it's a
            # function.  We do this to avoid function call overhead of
            # injecting another wrapper.  However, we must wrap if the
            # function is a bound method because we can't set attributes on a
            # bound method.
            if is_bound_method(view):
                _mapped_view = mapped_view
                def mapped_view(context, request):
                    return _mapped_view(context, request)
            if self.attr is not None:
                mapped_view.__text__ = 'attr %s of %s' % (
                    self.attr, object_description(view))
pyramid/tests/test_config/test_views.py
@@ -230,6 +230,18 @@
        result = wrapper(None, None)
        self.assertEqual(result, 'OK')
    def test_add_view_as_instancemethod_requestonly(self):
        from pyramid.renderers import null_renderer
        class View:
            def index(self, request):
                return 'OK'
        view = View()
        config=self._makeOne(autocommit=True)
        config.add_view(view=view.index, renderer=null_renderer)
        wrapper = self._getViewCallable(config)
        result = wrapper(None, None)
        self.assertEqual(result, 'OK')
    def test_add_view_as_instance_requestonly(self):
        from pyramid.renderers import null_renderer
        class AView: