WebPie URI-to-web method mapping, detailed

Here is what actually happens when a WebPie application receives a request for a URI like this: /a/b/c/…/d?x=y&z=q:

  1. Create a WebOb Request object and let it parse the WSGI environment.

  2. Extract the URI path (/a/b/c/…/d) and the query arguments (x=y&z=q) from the WebOb object.

  3. Creates arguments dictionary {"x":"y", "z":"q"}.

  4. Break the URI path into pieces ["a","b","c",..."d"].

  5. Set unseen_path to ["a","b","c",..."d"].

  6. Create the top Handler object and make the top Handler current handler.

  7. Loop:

    1. If the current handler is callable, set callable to the current handler break out of the loop.

    2. If the unseen path is empty, exit with error – invalid path

    3. Remove first element from the unseen_path and remember it as method

    4. If the current handler is a WPHandler object and it has an attribute named as method:

      • Set current handler to the method of the current handler
      • continue the Loop
    5. Otherwise, exit with error

  8. join unused path into relpath and call the callable:

    response = callable(request,    # original WebOb Request object
        "/".join(unused_path),      # joined unused path
        **arguments                 # query arguments
    )
    

In plain English: URI is mapped to actual web method by going down the tree of WPHandler obcjets starting from the top Handler, looking for first callable and calling it as a web method, passing it the WebOb Request, unused portion of the path and query arguments.

Examples

class H1(WPHandler):

    def say(self, request, relpath, what="hello", **therest):
        # only "what" query argument will be used.
        # The rest will be accepted, but ignored
        return relpath or what

class Hander(WPHandler):

    def __init__(self, *params):
        WPHandler.__init__(self, *params)
        self.down = H1(*params)

    def hello(self, request, relpath, **args):
        return relpath

    def no_args(self, request, relapth):
        return "OK"

    def few_args(self, request, relapth, x=None, b="yes"):
        return "OK"

app = WPApp(Hander)

This handler will respond to URIs:

/hello                  # relpath="",       args={}
/hello/there            # relpath="there",  args={}
/hello?a=b              # relpath="",       args={"a":"b"}
/hi                     # error - web method not found
/no_args                # relpath=""
/no_args?x=y            # error - the web method does not expect
                        #         any query arguments
/few_args/abc?x=y       # relpath = "abc",  x="y", b="yes"
/few_args?b=no          # relpath = "",     x=None, b="no"
/down/say?what=hi       # relpath = "",     what="hi",      reply: "hi"
/down/say?x=unused      # relpath = "",     what="hi",      reply: "hi"
/down/say/hi            # relpath = "hi",   what="hello",   reply: "hi"
/down/say               # relpath = "",     what="hello",   reply: "hello"

A Handler, which will accept any URI:

class AcceptAll(WPHandler):

    def __call__(self, request, relpath, **args):
        return f"""
            relpath:    {relpath}\n
            query args: {args}\n
        """

app = WPApp(AcceptAll)

In simple cases, there is no need to create WPHandler explicitly:

def hello_world(request, relpath, **args):
    from = f" from {relpath}" if relpath else ""
    return f"Hello World!{form}"

WPApp(hello_world).run_server(8080)

Or even shorter, but less readable, unless you are into functional programming:

from webpie import WPApp

WPApp(lambda q,p,x: f"{(((int(x)+19)**3)%101)}").run_server(8888)