Cornices/cornice

Make Service decorators work with Class Based Views

Opened this issue · 2 comments

ergo commented

Currently it is impossible to use Cornice decorators on CBV's (except @resource decorator),

This is the feedback I got from pyramid developers on what we can do to improve the situation, leaving here for reference.

yeah if I had to guess cornice is only registering a venusian scan for the service                                                                                                           
then cornice itself calls config.add_route and config.add_view directly                                                                                                                      
the decorator is just handled in the service directly                                                                                                                                        
that is my impression... so then it's up to cornice to deal with it itself                                                                                                                   
if you want to keep things structured that way at least internally... 
instead of making them venusian decorators on the methods                                                              
for example @foo.get() could be a venusian decorator that under the hood calls 
config.add_resource(foo, type='get', ...)                                                                     
which would be an action that executes before add_view/add_route                                                                                                                             
this is how you'd restructure the config into pyramid actions at least                                                                                                                       
but atm cornice does all the config itself at import-time and then just 
registers with pyramid at scan-time... cornice doesn't register its own 
fine-grained actions that to all the work on the service                                                                                                                                                                                  

Side note: BIG GOTCHA to be aware of:
Pyramid IRC folks confirmed it.

I added the auto-wiring of factory instance to support pyramidic ACL to @resource decorated classes and came to discover something interesting about that (#456)

The following construct will cause BaseResource.__init__(self, request) to be called "twice". Once for the instance of the factory and the other for the instance of the view. Pyramid won't care but the unaware coder (me) of a Resource would be very confused especially if __init__ does some setup that's meant to happen only once.

@resource(collection_path='/items', path='/items/{uuid}')
class Items(BaseResource):
    @view(permission='edit')
    def get(self):
        logging.error("GET")
        ...
 
class BaseResource:
def __acl__(self, context=None):
    ...
def __init__(self, request):
    logging.error("BaseResource %r %r ", self, request)  # TWO CALLS => 2 INSTANCES!!!
    self.request = request 
    ...

Output will be the following:

default.py  54 ERROR  BaseResource <xserver.views.default.Items object at 0x1116a8d68> <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>
default.py  46 ERROR  __acl__ <xserver.views.default.Items object at 0x1116a8d68> <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>
default.py  54 ERROR  BaseResource <xserver.views.default.Items object at 0x11168db70> <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>
default.py 183 ERROR  GET <Request at 0x1116a8278 GET http://localhost/items/0D057D01-B3DE-450A-9A68-9BB8AC3E4DC3>

can confirm with pattern like:

class Root(object):
    __name__ = 'Root'
    __parent__ = None

    def __acl__(self):
        return [
            (Allow, Authenticated, MINIMAL),
           ...
        ]


@resource(collection_path='/items',  path='/items/{id}',
          factory=Root)
class ItemView(object):

    def __init__(self, request, context=None):
        self.request = request