WebPie is a simple, intuitive, object-oriented web applications development framework for Python. WebPie helps you develop web applications quickly and structure them in terms of classes to organize your code and development effort more efficiently.

Hello, world!

Here is the shortest web server application created with WebPie

                

                    from webpie import WPApp                                  #1
                    
                    def hello(request, relpath, **args):                      #2
                        return "Hello, World!\n"                              #3
                    
                    WPApp(hello).run_server(8080)                             #4


                
                

Here is what we just did, line by line:

  • #1 -- Import WebPie aplication class definitions.
  • #2 -- Define simple request handler funciton.
  • #3 -- The function simply returns constant response.
  • #5 -- Create and run your web server application.

This simple server will respond to any URI with constant response, regarless of the URI.

The hello handler function is a web method. In WebPie a web method always has two positional arguments and some keyword arguments. The two positional arguments are:

request
a WebOb Request object. WebPie parses incoming HTTP request and stores its information into a WebOb request.
relpath
potion of the URI path left unused after WebPie translated the URI path into actual web method. In this trivial example, no translation is actually done and relpath will always hold the entire URI path portion since none of it will be used. In the real life, relpath will hold the tail of the URI path, and usually will be empty.
keyword arguments
if the request URI had some query arguments like a=2&x=y, then the web method would be called with parsed query parameters passed as the keyword arguments.

Here is a bit more sophisticated example illustrating some of the web method features

                

                    from webpie import WPApp                                  
                    
                    def hello(request, relpath, who="world"):                 #1
                        my_name = relpath                                     #2
                        return f"Hello, {who}! I am {my_name}\n"              #3
                    
                    WPApp(hello).run_server(8080)                             

                
                

  • #1 -- the method will accept optional who CGI query argument.
  • #2 -- relpath will be used to pass server's own name.
  • #3 -- The response will include both pieces.

If you run this script, it will respond to requests like this:

                
                    $ curl "http://localhost:8080/webpie"
                    Hello, world! I am webpie
                    
                    $ curl "http://localhost:8080/webpie?who=User"
                    Hello, User! I am webpie
                    
                    $ curl "http://localhost:8080/server?who=User"
                    Hello, User! I am server
                    

                
                

Building a WebPie application directly from a web method is quick and simple, but it gives you access to a very limited portions of WebPie functionality. If you want to take full advantage of WebPie features and build more sophisticated applications, you will need to use WPHandler class instead of a Python function like hello.

Here is a bit more sophisticated web application using WPHandler class:

                

                    from webpie import WPApp, WPHandler
                    from datetime import datetime, date

                    class DateTimeHandler(WPHandler):   
    
                        def date(self, request, relpath, part=None):
                            #
                            # will respond to .../date?part=(year|month|day)
                            #
                            today = date.today()
                            if part == "year":
                                return str(today.year)+"\n"
                            elif part == "month":
                                return str(today.month)+"\n"
                            elif part == "day":
                                return str(today.day)+"\n"
                            else:
                                return str(today)+"\n"

                        def time(self, request, relpath):                              
                            #
                            # will respond to .../time/(hour|minute|second)
                            #
                            t = datetime.now()
                            if relpath == "hour":
                                    return str(t.hour)+"\n"
                            elif relpath == "minute":
                                    return str(t.minute)+"\n"
                            elif relpath == "second":
                                    return str(t.second)+"\n"
                            else:
                                    return str(t.time())+"\n"

                    WPApp(DateTimeHandler).run_server(8080)

                
                

This application is built using a WPHandler class with two web methods, date and time. The way WebPie request routing works, a URI like ".../date" will invoke the date method of the handler, and a URI like ".../time" will be processed by the time method.

These two methods illustrate using 2 different mechanisms to get the query parameters. The time method uses relpath to get the part of the time to extract. Whereas the date method gets the part of the date from the CGI query. So this server will respond to URIs like these:

                
                    $ curl "http://localhost:8080/time/hour"
                    08
                    
                    $ curl "http://localhost:8080/time/minute"
                    18
                    
                    $ curl "http://localhost:8080/time/time"
                    08:18:27.798931
                    
                    $ curl "http://localhost:8080/date?part=month"
                    3
                    
                    $ curl "http://localhost:8080/date?part=year"
                    2021
                    
                    $ curl "http://localhost:8080/date"
                    2021-03-02
                
                

WSGI

WebPie application is a standard WSGI application object. It can be plugged into any HTTP server which accepts WSGI applications, such as nginx/uWSGI or Apache httpd/mod_wsgi. For example, here is how you can run a WebPie application under uWSGI:

                
                    # clock.py

                    from webpie import WPApp, WPHandler                       
                    import time

                    class Clock(WPHandler):                                

                        def time(self, request, relpath):          
                            return time.ctime()

                    application = WPApp(Clock)                             

                
                
                
                    $ uwsgi --http :8080 --wsgi-file clock.py &
                    
                    $ curl http://localhost:8080/time
                    Fri Jun 19 11:26:53 2020
                    $

                
                

URI Structure

If you want to combine the Greeter and the Clock into a single web server, you can do it by builidng a tree of the request handlers:

                
                    from webpie import WPApp, WPHandler                       
                    import time

                    class Clock(WPHandler):                                

                        def time(self, request, relpath):          
                            return time.ctime()

                    class Greeter(WPHandler):                                
                    
                        def hello(self, request, relpath, **args):           
                            return "Hello, World!\n"           
                            
                    class Combined(WPHandler):
                    
                        def __init__(self, app):
                            WPHandler.__init__(self, request, app)
                            self.clock = Clock(request, app)
                            self.greet = Greeter(request, app)

                    WPApp(Combined).run_server(8080)                         

                
                

If you run this application, it will respond to both kinds of requests and the URI structure will reflect the nested handler structure:

                
                    $ curl http://localhost:8080/clock/time
                    Fri Jun 19 11:26:53 2020
                    
                    $ curl http://localhost:8080/greet/hello
                    Hello, World!
                    $
                    
                
                

Notice that URI path element clock maps to the Combined's self.clock member, and similarily URI element greet corresponds to the Combined's self.greet member.

The fact that URI structure maps directly onto the handler's class structure and that each handler is responsible for its own URI path subtree makes it possible to use relative URIs inside the response generated by the handler. For example, let's say you have a handler responsible for user authentication:

                
                    from webpie import WPApp, WPHandler                       

                    class Auth(WPHandler):                                
                    
                        def login_form(self, request, relpath, message=None, **args):           
                            return login_form
                            
                        def do_login(self, request, relpath, **args):
                            # read the login_form and authenticate the user
                            if authenticated:
                                # login OK - go back to the top handler
                                self.redirect("../index")       
                            else:
                                # try again
                                self.redirect("./login_form?message=Login+failed")
                                
                    class Top(WPHandler):
                    
                        def __init__(self, request, app):
                            WPHandler.__init__(self, request, app)
                            self.auth = Auth(request, app)
                            
                        def index(self, request, relpath, **args):
                            # main entry point
                            

                    app = WPApp(Top)                        

                
                

The Top handler can use relative URI "./auth/login_form" to refer to the login form shown by the authentication handler. And the login form can refer to the actual authentication menthod do_login as "./do_login":

                    
                    
                        <html>
                        <!-- login form -->
                        ...
                            <form action="./do_login">
                                ...
                            </form>
                        ...
                        </html>
                    
                
                    
                    
                        <html>
                        <!-- main page -->
                        ...
                            <a href="./auth/login_form">log in<a>
                        ...
                        </html>
                    
                

The Shortest Web Service

And here, just for fun, is probably the shortest web service possible. And yes, it is a bit hard to read, but it works:

                
                    from webpie import WPApp
                    WPApp(lambda _, x: f"{int(x)**2}\n").run_server(8888)
                
                
                
                    $ curl http://localhost:8888/2
                    4
                    $ curl http://localhost:8888/3
                    9
                    $ curl http://localhost:8888/4
                    16
                 
                 

Other Features

  • WebPie uses WebOb to parse and represent the received HTTP request and to produce the response
  • WebPie lets you build multi-threaded web services. Each (concurrent) HTTP request is handled by its own instance of the handler class, which uses its own WebOb Request object as input. This way each HTTP request is handled independently and in isolation from each other. But if needed, portions of the code can syncronize using shared persistent WPApp object.
  • WebPie provides sessions with persistent data storage, which persists between the requests
  • WebPie is Jinja2 - friendly, but does not require Jinja2 to be installed
  • WebPie comes with simple multi-treaded HTTP/HTTPS server with thread control, which can run on the main thread or as another thread.
  • WebPie supports access to static content
  • WebSockets support is included in WebPie