pyramid: how to customize when/how/if a view is called?

53 views
Skip to first unread message

Thomas G. Willis

unread,
Jan 1, 2013, 4:48:31 PM1/1/13
to pylons...@googlegroups.com
  I'm maintaining a json api with pyramid. And one of the request I
  get on occasion is some way to provide help docs on demand for a
  given endpoint. Something like.....


  /user/login?email=...&password=...

  which maps to a UserModel context with view name = "login"
  would normally call the view but ideally it would be nice to provide
  something like....

  /user/login/__help__ or /user/login?__help__


  if it could return the docstring from the function, the permission
  required, whether the user has the permission or not, and any
  paramaters expected on the request that would be a good start.

  So a response like

   {view:"login",permitted:true, "permission_required":"anonymous",
   parameters:["email","password"]}

  So it seems I need a way to hook into pyramid at the point that the
  context and view are selected, but because the view may be secured i
  need a way to look determine whether it's a call to the view or a
  call to get documentation about the view.

  Since there is no IViewSelected event to subscribe to, my first
  thought was to subscribe to the IContextFound event and take a bit
  of the code from pyramid.views to go through the view selection but
  not call it. This way, I can handle the case where a view
  documentation request comes in.

  The problem is, that this event occurs in a very delicate place
  inside pyramid. I can't raise a new Context and expect the view
  selection machinery to pick it up and do what I'm expecting.

  So, due to copy/pasting code out of pyramid and seeing solutions
  that monkey patch pyramid to get what I want, I'm assuming I'm doing
  it wrong. So, I'm asking if there's a pyramid way to provide what I
  want. I've got the view paramater stuff figured out, I just would
  like to know if there's a pattern I can use to customize what
  happens between after the context is found, and after a view is
  found but before it is ran.

Thomas G. Willis

unread,
Jan 2, 2013, 7:55:30 AM1/2/13
to pylons...@googlegroups.com
Well looks like overriding the traverser is a way to do it..... not too thrilled about copying source from pyramid to tweak but I think I can live with this. 


class BatteriiTraverser(ResourceTreeTraverser):
    HELP_TOKEN = "@@help@@"

    def __call__(self, request):
        needs_help = request.path_info.endswith(self.HELP_TOKEN)
        if needs_help:
            request.path_info = request.path_info.replace(self.HELP_TOKEN, "")
            result = ResourceTreeTraverser.__call__(self, request)
            view = self._get_view_for_context(result["context"], request, result["view_name"])
            if view:
                result["context"] = model.help.HelpRequest(result["context"], request, view)
                result["view_name"] = ""
            else:
                result["context"] = HTTPNotFound()
                result["view_name"] = ""
        else:
            result = ResourceTreeTraverser.__call__(self, request)
        return result


    def _get_view_for_context(self, context, request, name=""):
        """
        taken from pyramid because there's no way to hook into pre/post view selection
        """
        provides = [IViewClassifier] + map_(providedBy, (request, context))
        try:
            reg = request.registry
        except AttributeError:
            reg = get_current_registry()
        return reg.adapters.lookup(provides, IView, name=name)

Arndt Droullier

unread,
Jan 11, 2013, 6:51:36 AM1/11/13
to pylons...@googlegroups.com
Late, but maybe still useful...

I just thought about a similar setup: Providing apis in multiple versions, routing and so on.

The solution I came up with is to use some kind of "VirtualRoot" objects in pyramid traversal
and register views with containment predicates. 
Something like:


class VirtualRoot(object):
    """ add a named 'virtual' root object in traversal tree for url generation """
    __parent__ = None
    __name__ = u""

    def __init__(self, name, original_root):
        original_root.__name__ = name
        original_root.__parent__ = self
        self.original_root = original_root        
    
    def __getitem__(self, name):
        if name != self.original_root.__name__:
            raise KeyError, name
        return self.original_root
    

Inserted as the traversal root and passed a name and the original root object this 
approach is 100% pyramid compatible. A subclass Api1, Api2 or ApiDoc can be
used in view registrations register_view(..., containment=Api1, ...) to do the rest 
for you.

Arndt.


2013/1/1 Thomas G. Willis <tom.w...@gmail.com>

  I'm maintaining a json api with pyramid. And one of the request I
  get on occasion is some way to provide help docs on demand for a
  given endpoint. Something like.....


 

--
Arndt Droullier / Nive cms cms.nive.co
Reply all
Reply to author
Forward
0 new messages