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
|