From 697213201fbd258e38eec92b80e0a1c631733ada Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sat, 7 Feb 2015 03:31:33 -0700 Subject: Add extra tests for sub-classing Request --- pyramid/tests/test_request.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index 48af98f59..7134e3869 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -435,6 +435,34 @@ class Test_call_app_with_subpath_as_path_info(unittest.TestCase): self.assertEqual(request.environ['SCRIPT_NAME'], '/' + encoded) self.assertEqual(request.environ['PATH_INFO'], '/' + encoded) +class Test_subclassing_Request(unittest.TestCase): + + def test_subclass(self): + from pyramid.request import Request + from zope.interface import providedBy, implementedBy + + class RequestSub(Request): + pass + + self.assertTrue(hasattr(Request, '__provides__')) + self.assertFalse(hasattr(RequestSub, '__provides__')) + + def test_subclass_with_implementer(self): + from pyramid.interfaces import IRequest + from pyramid.request import Request + from zope.interface import providedBy, implementedBy, implementer + + @implementer(IRequest) + class RequestSub(Request): + pass + + self.assertTrue(hasattr(Request, '__provides__')) + self.assertTrue(hasattr(RequestSub, '__provides__')) + + req = RequestSub({}) + req._set_properties({'b': 'b'}) + self.assertEqual(providedBy(req), implementedBy(RequestSub)) + class DummyRequest: def __init__(self, environ=None): if environ is None: -- cgit v1.2.3 From 942e7dcaac03f8910ecef19bb4c610cf6e79ac76 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sat, 7 Feb 2015 12:48:05 -0700 Subject: Add more asserts --- pyramid/tests/test_request.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index 7134e3869..cf591eb36 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -438,6 +438,7 @@ class Test_call_app_with_subpath_as_path_info(unittest.TestCase): class Test_subclassing_Request(unittest.TestCase): def test_subclass(self): + from pyramid.interfaces import IRequest from pyramid.request import Request from zope.interface import providedBy, implementedBy @@ -445,7 +446,16 @@ class Test_subclassing_Request(unittest.TestCase): pass self.assertTrue(hasattr(Request, '__provides__')) + self.assertTrue(hasattr(Request, '__implemented__')) + self.assertTrue(hasattr(Request, '__providedBy__')) self.assertFalse(hasattr(RequestSub, '__provides__')) + self.assertTrue(hasattr(RequestSub, '__providedBy__')) + self.assertTrue(hasattr(RequestSub, '__implemented__')) + + self.assertTrue(IRequest.implementedBy(RequestSub)) + # The call to implementedBy will add __provides__ to the class + self.assertTrue(hasattr(RequestSub, '__provides__')) + def test_subclass_with_implementer(self): from pyramid.interfaces import IRequest @@ -457,11 +467,18 @@ class Test_subclassing_Request(unittest.TestCase): pass self.assertTrue(hasattr(Request, '__provides__')) + self.assertTrue(hasattr(Request, '__implemented__')) + self.assertTrue(hasattr(Request, '__providedBy__')) self.assertTrue(hasattr(RequestSub, '__provides__')) + self.assertTrue(hasattr(RequestSub, '__providedBy__')) + self.assertTrue(hasattr(RequestSub, '__implemented__')) req = RequestSub({}) req._set_properties({'b': 'b'}) - self.assertEqual(providedBy(req), implementedBy(RequestSub)) + + self.assertTrue(IRequest.providedBy(req)) + self.assertTrue(IRequest.implementedBy(RequestSub)) + class DummyRequest: def __init__(self, environ=None): -- cgit v1.2.3 From c7c02f44492da19d848d66556c2c13b9c79ee7ee Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sat, 7 Feb 2015 12:48:30 -0700 Subject: Add test that showcases failure When _set_properties() is called before implementedBy or providedBy, the new class that is generated does NOT conform to IRequest. --- pyramid/tests/test_request.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index cf591eb36..1a7b1a106 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -479,6 +479,20 @@ class Test_subclassing_Request(unittest.TestCase): self.assertTrue(IRequest.providedBy(req)) self.assertTrue(IRequest.implementedBy(RequestSub)) + def test_subclass_mutate_before_providedBy(self): + from pyramid.interfaces import IRequest + from pyramid.request import Request + from zope.interface import providedBy, implementedBy, implementer + + class RequestSub(Request): + pass + + req = RequestSub({}) + req._set_properties({'b': 'b'}) + + self.assertTrue(IRequest.providedBy(req)) + self.assertTrue(IRequest.implementedBy(RequestSub)) + class DummyRequest: def __init__(self, environ=None): -- cgit v1.2.3 From c600ab6fe31e2ea95f1aea9f8c40ae585f6afe39 Mon Sep 17 00:00:00 2001 From: Bert JW Regeer Date: Sat, 7 Feb 2015 12:57:11 -0700 Subject: Remove __providesBy__ attribute from being copied This change reverts part of what https://github.com/Pylons/pyramid/issues/1212 changed. We attempt to copy information used by zope.interfaces to the new class to avoid creating new objects that could possibly leak. Unfortunately copying over __providedBy__ causes issues with the sub-class after modification no longer implementing interfaces, for more information see: https://github.com/Pylons/pyramid/issues/1529#issuecomment-73373581 Once __providedBy__ is removed from the list, this is no longer the case and the new class that is created in `_set_properties` properly report that it implements the interfaces it used to implement before. --- pyramid/tests/test_request.py | 1 - pyramid/util.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pyramid/tests/test_request.py b/pyramid/tests/test_request.py index 1a7b1a106..091454893 100644 --- a/pyramid/tests/test_request.py +++ b/pyramid/tests/test_request.py @@ -436,7 +436,6 @@ class Test_call_app_with_subpath_as_path_info(unittest.TestCase): self.assertEqual(request.environ['PATH_INFO'], '/' + encoded) class Test_subclassing_Request(unittest.TestCase): - def test_subclass(self): from pyramid.interfaces import IRequest from pyramid.request import Request diff --git a/pyramid/util.py b/pyramid/util.py index 18cef4602..7903326a5 100644 --- a/pyramid/util.py +++ b/pyramid/util.py @@ -87,10 +87,10 @@ class InstancePropertyMixin(object): if attrs: parent = self.__class__ cls = type(parent.__name__, (parent, object), attrs) - # We assign __provides__, __implemented__ and __providedBy__ below - # to prevent a memory leak that results from from the usage of this - # instance's eventual use in an adapter lookup. Adapter lookup - # results in ``zope.interface.implementedBy`` being called with the + # We assign __provides__ and __implemented__ below to prevent a + # memory leak that results from from the usage of this instance's + # eventual use in an adapter lookup. Adapter lookup results in + # ``zope.interface.implementedBy`` being called with the # newly-created class as an argument. Because the newly-created # class has no interface specification data of its own, lookup # causes new ClassProvides and Implements instances related to our @@ -99,7 +99,7 @@ class InstancePropertyMixin(object): # want this new class to behave exactly like it is the parent class # instead. See https://github.com/Pylons/pyramid/issues/1212 for # more information. - for name in ('__implemented__', '__providedBy__', '__provides__'): + for name in ('__implemented__', '__provides__'): # we assign these attributes conditionally to make it possible # to test this class in isolation without having any interfaces # attached to it -- cgit v1.2.3