You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Describe the bug
View lookup using context= does not support abstract base classes in Pyramid 1.10.4 on Python 3.8.3.
To Reproduce
Experiment with the following code:
# A CoolClass has an attribute is_cool = TrueclassCoolClass(ABC):
is_cool=True# This abstract base class does not require subclassing it# Any class with an attribute is_cool = True is a CoolClass@classmethoddef__subclasshook__(cls, other):
ifgetattr(other, "is_cool", None) isTrue:
returnTrueelse:
returnFalse# Add a view that handles instances of CoolClass@view_config(context=CoolClass)defcool_view(context, request):
return"Too cool!"# Let's make a traversal resourceclassMyCoolClass:
is_cool=Truedef__init__(self, req):
self.request=req# Let's check that MyCoolClass is a subclass of CoolClassprint(issubclass(MyCoolClass, CoolClass)) # True# Let's check that an instance of MyCoolClass is an instance of CoolClasscc=MyCoolClass(None)
print(isinstance(cc, CoolClass)) # True
Now set up a Pyramid application with MyCoolClass in the resource tree. You will find that Pyramid will return 404 due to no view callable being found.
Now let's try with a less realistic use case:
classCoolClass(ABC):
is_cool=True# CoolClass's __subclasshook__ now has an additional check# If the class has an attribute not_cool = True, it is not a subclass no matter what!@classmethoddef__subclasshook__(cls, other):
ifgetattr(other, "not_cool", None) isTrue:
returnFalseelifgetattr(other, "is_cool", None) isTrue:
returnTrueelse:
returnFalse# Now let's make a resource explicitly subclassing CoolClassclassNotCoolClass(CoolClass):
is_cool=Truenot_cool=True# Makes this class NOT a subclass of CoolClassdef__init__(self, req):
self.request=req# Let's check that NotCoolClass is NOT a subclass of CoolClassprint(issubclass(NotCoolClass, CoolClass)) # False# Let's check that an instance of NotCoolClass is NOT an instance of CoolClasscc=NotCoolClass(None)
print(isinstance(cc, CoolClass)) # False
Now set up a Pyramid application with a NotCoolClass resource in the tree. You will find that Pyramid matches cool_view even though NotCoolClass is NOT a CoolClass.
Expected behavior
When view lookup happens with a MyCoolClass as the context, cool_view should be selected. Instead, no appropriate view callable is found.
Additional context
The documentation for view configuration states:
An object representing a Python class of which the context resource must be an instance or the interface that the context resource must provide in order for this view to be found and called. This predicate is true when the context resource is an instance of the represented class or if the context resource provides the represented interface; it is otherwise false.
I have confirmed that MyCoolClass is in fact an instance of CoolClass. isinstance() on an instance of MyCoolClass returns True, and issubclass() of its type also returns True. As far as I can tell, the predicate for context= should return True and select this view callable.
Note that everything behaves correctly if MyCoolClass explicitly subclasses CoolClass. I am assuming Pyramid explicitly looks for CoolClass in its hierarchy instead of just using isinstance/issubclass.
All that said, I don't even know if this is a good thing to support or if its a good idea to select views this way, it's just confusing that Pyramid/isinstance disagree. On one hand, if a programmer declares class MyCoolClass:, they might find it surprising that isinstance returns True on something other than itself/object. On the other hand, if isinstance returns True on an object, they might find it surprising that a Pyramid view does not match it. Supporting this case, though, means that one could write some really advanced predicates using just the context= argument.
The text was updated successfully, but these errors were encountered:
This is probably because zope.interface doesn't support implicit ABCs and I have no clue if that's on their roadmap. Personally I'm not a big fan of them so getting them supported is not high on my list but that is more than likely the issue here. Pyramid defers to the zope component registry to traverse the class SRO and then lookup instances by those interfaces.
It is related to getting the following chunk of code to work:
Describe the bug
View lookup using
context=
does not support abstract base classes in Pyramid 1.10.4 on Python 3.8.3.To Reproduce
Experiment with the following code:
Now set up a Pyramid application with MyCoolClass in the resource tree. You will find that Pyramid will return 404 due to no view callable being found.
Now let's try with a less realistic use case:
Now set up a Pyramid application with a NotCoolClass resource in the tree. You will find that Pyramid matches
cool_view
even though NotCoolClass is NOT a CoolClass.Expected behavior
When view lookup happens with a MyCoolClass as the context, cool_view should be selected. Instead, no appropriate view callable is found.
Additional context
The documentation for view configuration states:
I have confirmed that MyCoolClass is in fact an instance of CoolClass.
isinstance()
on an instance of MyCoolClass returns True, andissubclass()
of its type also returns True. As far as I can tell, the predicate forcontext=
should return True and select this view callable.Note that everything behaves correctly if MyCoolClass explicitly subclasses CoolClass. I am assuming Pyramid explicitly looks for CoolClass in its hierarchy instead of just using isinstance/issubclass.
All that said, I don't even know if this is a good thing to support or if its a good idea to select views this way, it's just confusing that Pyramid/isinstance disagree. On one hand, if a programmer declares
class MyCoolClass:
, they might find it surprising thatisinstance
returns True on something other than itself/object. On the other hand, ifisinstance
returns True on an object, they might find it surprising that a Pyramid view does not match it. Supporting this case, though, means that one could write some really advanced predicates using just thecontext=
argument.The text was updated successfully, but these errors were encountered: