Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upView selection breaks when using multiple base classes #1552
Comments
wichert
changed the title from
View selection breaks when using both ABCs and normal base classes
to
View selection breaks when using both multiple base classes
Feb 2, 2015
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
wichert
Feb 2, 2015
Member
The use of ABCs turns out to be a red herring: I get the exact same error if I use normal base classes.
|
The use of ABCs turns out to be a red herring: I get the exact same error if I use normal base classes. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
wichert
Feb 2, 2015
Member
Simplest possible test case:
from webtest import TestApp
from pyramid.config import Configurator
class OtherBase(object): pass
class Int1(object): pass
class Int2(object): pass
class Resource(OtherBase, Int1, Int2):
def __init__(self, request):
pass
def unknown(context, request):
return 'unknown'
def view(context, request):
return 'hello'
def test_views_for_multiple_base_classes():
config = Configurator()
config.add_route('root', '/', factory=Resource)
config.add_view(unknown, route_name='root', renderer='string')
config.add_view(view, route_name='root', renderer='string',
context=Int1, request_method='GET')
config.add_view(view, route_name='root', renderer='string',
context=Int2, request_method='POST')
app = TestApp(config.make_wsgi_app())
assert 'hello' in app.get('/')
assert 'hello' in app.post('/')
assert 'unknown' in app.delete('/')|
Simplest possible test case: from webtest import TestApp
from pyramid.config import Configurator
class OtherBase(object): pass
class Int1(object): pass
class Int2(object): pass
class Resource(OtherBase, Int1, Int2):
def __init__(self, request):
pass
def unknown(context, request):
return 'unknown'
def view(context, request):
return 'hello'
def test_views_for_multiple_base_classes():
config = Configurator()
config.add_route('root', '/', factory=Resource)
config.add_view(unknown, route_name='root', renderer='string')
config.add_view(view, route_name='root', renderer='string',
context=Int1, request_method='GET')
config.add_view(view, route_name='root', renderer='string',
context=Int2, request_method='POST')
app = TestApp(config.make_wsgi_app())
assert 'hello' in app.get('/')
assert 'hello' in app.post('/')
assert 'unknown' in app.delete('/') |
wichert
changed the title from
View selection breaks when using both multiple base classes
to
View selection breaks when using multiple base classes
Feb 2, 2015
mmerickel
added
the
bugs
label
Feb 2, 2015
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
mmerickel
Feb 2, 2015
Member
Yeah this looks like a bug. I can confirm what you're seeing on 1.5-branch and master.
|
Yeah this looks like a bug. I can confirm what you're seeing on 1.5-branch and master. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
wichert
Feb 4, 2015
Member
I seem to have found a workaround: instead of registering the unknown view without a request_method I can register it for all request methods separately:
for method in ['DELETE', 'GET', 'PATCH', 'POST', 'PUT']:
config.add_view(unknown, route_name='root', request_method=method, renderer='string')|
I seem to have found a workaround: instead of registering the for method in ['DELETE', 'GET', 'PATCH', 'POST', 'PUT']:
config.add_view(unknown, route_name='root', request_method=method, renderer='string') |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
I spoke too soon: that workaround also doesn't work :( |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
sontek
Feb 4, 2015
Member
So I believe this is a legit problem with your code and not Pyramid (although an unfortunate one) because the traverser is going to resolve the OtherBase class first, since its the first in the inheritance chain and since that will perfectly match the unknown view it calls it.
If you move the OtherBase inheritance to the end of the chain the code will work because the traverser will match the classes that will hit the other views first.
|
So I believe this is a legit problem with your code and not Pyramid (although an unfortunate one) because the traverser is going to resolve the If you move the |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
wichert
Feb 4, 2015
Member
I don't think this is a problem with my code: the view logic should find the most specific view for the route. In this case it can choose between a view which only filters on route name, and a view which filters on route name, context class and request method, but it still picks the first one.
Reordering the inheritance order is not possible: the other classes are ABCs, and if you list those first their abstract methods will override the real method implementations, breaking the entire class.
|
I don't think this is a problem with my code: the view logic should find the most specific view for the route. In this case it can choose between a view which only filters on route name, and a view which filters on route name, context class and request method, but it still picks the first one. Reordering the inheritance order is not possible: the other classes are ABCs, and if you list those first their abstract methods will override the real method implementations, breaking the entire class. |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
sontek
Feb 4, 2015
Member
@wichert The chunk of code that is causing this is:
https://github.com/Pylons/pyramid/blob/master/pyramid/router.py#L164-L181
You would have to change that chunk to detect most specific rather than going in order
|
@wichert The chunk of code that is causing this is: You would have to change that chunk to detect most specific rather than going in order |
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
Pushed the fix as a PR |
sontek
referenced this issue
Feb 4, 2015
Closed
Rework route to use views with most predicates first. #1553
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
This is fixed as the result of #1557 |
mcdonc
closed this
Apr 6, 2015
This comment has been minimized.
Show comment
Hide comment
This comment has been minimized.
|
I'm very happy to hear that. Thanks! |
wichert commentedFeb 2, 2015
rest-toolkit uses ABCs to add default behaviour to resources. For example if a resource class adds the
ViewableResourceabc to its base classes the default GET view from rest-toolkit will be used for that resource. Under the hood this is implemented by adding views for the ABC to the route. The user can then also add a view for the exact resource class to override the default view.Unfortunately this system breaks if a resource uses multiple ABCs as well as a non-abc base class. This is a test case that demonstrates this:
The failure is:
If you remove the
OtherBasebase class fromResourcethe test does not fail.