This tutorial will explain step by step how to get started with Karrigell, a simple yet full-featured, 100% Python web framework
This tutorial will explain step by step how to get started with Karrigell, a simple yet full-featured, 100% Python web framework
The key word for the development of Karrigell is simplicity :
- the package comes with “batteries included” : a web server and a database engine, in less than 1 MB of code
- no configuration : just unzip the package, start the server and go !
- programs are either pure Python, or a mix of Python and HTML a la PHP ; there is no templating syntax, requiring to learn custom tags
People who discover Karrigell are often surprised by this simplicity, and might think of it as a toy, only suited for an introduction and perhaps education on dynamic web servers. In fact, it lets you do the simple things in a simple way : if you want to write the proverbial “Hello, world !” program, all you have to type is
print “Hello, world !”But if you take a look at the documentation you might be surprised to see what the “toy” can do :
- easy access to HTML forms (that is, with minimum typing)
- inclusion of scripts inside other scripts
- very simple in-memory session management
- basic HTTP authentication
- HTTP redirection
- protection of directories (only accessible to authenticated users)
- a web interface to enter the translation of dynamic pages
- a variety of programming styles, from pure Python with a whole site in one script to PHP-like, one-script-for-each-page style
- inclusion of the Cheetah templating system
- a very fast asynchronous built-in web server
- virtual hosts support
- gzip support
- the ability to run behind Apache or Xitami
- a very clear and complete debugger
- a database management system (a web interface lets you generate the database and its CRUD script)
and much more…
For a more complete information, see the documentation
1. Getting started
First, download the latest version of Karrigell and unzip it. Open a console window and start the built-in server by :
>python Karrigell.py
Use a text editor to save this line of Python code in a file called hello.py, in the directory webapps :
print “hello world !”
Then type http://localhost/hello.py in your web browser to see the result
By default, Python scripts are rendered as HTML code. If you want to use a plain text format you can set the Content-Type header in your code, like this :
RESPONSE[‘Content-Type’] = ‘text/plain’ print “hello world !”
This example shows how Karrigell works : the scripts are executed in a namespace where the programmer finds all he needs. RESPONSE is one of the names available in this namespace : it is a dictionary holding the HTTP response headers
2. HTML forms
Let’s see how you can retrieve the information submitted by an HTML form. Create this HTML piece of code and save it in hello.htm
<form action="hello.py"> Say hello to <input name="user"> <input type="submit" value="Ok"> </form>
This page will prompt the user to enter a name, and will submit it to the script hello.py
Here is how you can ask hello.py to print it :
print "Hello, " + _user
The namespace built by Karrigell when it receives the data sent by the browser has a variable called _user, a string with the value entered in the field user (note the leading underscore : this is necessary to avoid conflicts with Python keywords or built-in names)
Alternatively, you could use this syntax :
print "Hello, " + QUERY['user']
QUERY is a dictionary with names matching the fields in the submitted form
3. Logging and session management
If you want to identify the user throughout his navigation in the site, for instance to print his name when needed, the easiest technique is session management. Karrigell manages it with another built-in name, a function called Session(), which returns a Python object to which you can set any attribute
Edit your script hello.py :
print “Hello, “ + _user Session().user = _user print “””What would you like to order :
<form action=”order.py”>
CD <input type=”radio” name=”type” value=”CD”>
book <input type=”radio” name=”type” value=”book”>
<input type=”submit” value=”Ok”>
</form>”””
On the second line you have set the attribute user of the session object to the value _user. The session object is specific to a web browser, so that it is guaranteed to remain specific to one user and can be used by the other scripts to identify him
For instance you can use it in order.py :
print “Hello %s” %Session().user print “So you want to order a %s” %_type
4. Authentication
It is a very common task to check that a user is allowed to perform a certain task. For this, the user is asked to enter a login and a password, and the program looks in a database to see if this login/password is valid
Change the html page to ask for a login and a password instead of the user name :
<form action=”check.py”> Login <input name=”login”> <br>Password <input type=”password” name=”password”> <input type=”submit” value=”Ok”> </form>
Then in the script check.py you would have :
def is_valid(login,password):
# replace this with a check in the users database
return login == ‘login’ and password == ‘password’
if is_valid(_login,_password):
Session().login = _login
print “Hello %s <br>” %_login
print “””What would you like to order :
<form action=”order.py”>
CD <input type=”radio” name=”type” value=”CD”>
book <input type=”radio” name=”type” value=”book”>
<input type=”submit” value=”Ok”>
</form>”””
else:
print “Sorry, you are not allowed to access this page”
Since the session object has an attribute called login, in other scripts you can test if the user is valid by testing the existence of this attribute :
if hasattr(Session(),’login’): (show information for authorized users) else: (show information for unlogged visitors)
Alternatively, you could use the basic HTTP authentication : a popup window prompting for login and password. To
achieve this you have to call another function in the namespace, called Authentication(), like this:
def authTest():
(check if AUTH_USER and AUTH_PASSWORD are in the database)
Authentication(authTest,”Authentication test”,\
“Sorry, you are not allowed to access this page”)
print “””What would you like to order :
<form action=”order.py”>
CD <input type=”radio” name=”type” value=”CD”>
book <input type=”radio” name=”type” value=”book”>
<input type=”submit” value=”Ok”>
</form>”””
Authentication() takes 3 arguments :
- the function to call : this function has no argument, but uses the names AUTH_USER and AUTH_PASSWORD that were entered by the user in the popup window
- the “realm” : a string shown on top of the popup window
- the message to return if the authentication failed, that is, if the function did not return True
If the authentication succeeds, execution of the script can continue
5. Karrigell services
For the moment we have put each script in a different file. For short scripts this can become tedious and difficult to maintain
You can use Karrigell services : Python scripts where each function matches a URL. They must have the extension “ks”
Change the initial HTML page to :
<form action=”home.ks/check”> Login <input name=”login”> <br>Password <input type=”password” name=”password”> <input type=”submit” value=”Ok”> </form>
Then save this in a file called home.ks :
def check(login,password):
if _is_valid(login,password):
Session().login = login
print “Hello”,login,”<br>”
print “””What would you like to order :
<form action=”order”>
CD <input type=”radio” name=”Type” value=”CD”>
book <input type=”radio” name=”Type” value=”book”>
<input type=”submit” value=”Ok”>
</form>”””
else:
print “Sorry, you are not allowed to access this page”
def order(Type):
if not hasattr(Session(),”login”):
print “Sorry, you are not allowed to access this page”
else:
print “Ok %s, you want to order a %s” %(Session().login,Type)
def _is_valid(login,password):
# replace this with a check in the users database
return login == ‘login’ and password == ‘password’
The “action” in the HTML form is “home.ks/check”. This means that Karrigell will search the function check() in the script home.ks. Since the form has the fields “login” and “password”, the function must also have these names as arguments
In this function, we first test if the login and password are valid. This is done in a “private” function _is_valid() : “private” means that this function can’t be called by a URL (here home.ks/_is_valid). Private functions begin with an underscore
If the test succeeds, the function writes another form with action = “order”, matching the function order() in the same script. Since the form has a radiobutton field called “Type”, the function order() also has an argument called Type (notice that Type begins with an uppercase letter, to avoid confusion with the Python built-in type)
6. HTMLTags (generate HTML with Python code)
In the scripts we have written so far, the HTML code is produced by printing strings with the HTML tags inside
If you don’t like this, you can use a module called HTMLTags, which works like this :
from HTMLTags import print HTML(HEAD(TITLE(‘test’))+BODY(‘hello world’))
This will produce the following HTML code :
<HTML> <HEAD> <TITLE>test</TITLE> </HEAD> <BODY>hello world</BODY> </HTML>
You can rewrite the script home.ks like this :
from HTMLTags import
def check(login,password):
if _is_valid(login,password):
Session().login = login
print “Hello”,login,”<br>”
print “””What would you like to order :”””
print FORM(TEXT(‘CD’)+INPUT(Type=”radio”,name=”Type”,value=”CD”) + TEXT(‘book’)+INPUT(Type=”radio”,name=”Type”,value=”book”) + INPUT(Type=”submit”,value=”ok”), action=”order”)
else:
print “Sorry, you are not allowed to access this page”
def order(Type):
if not hasattr(Session(),”login”):
print “Sorry, you are not allowed to access this page”
else:
print “Ok %s, you want to order a %s” %(Session().login,Type)
def _is_valid(login,password):
# replace this with a check in the users database
return login == ‘login’ and password == ‘password’
7. Python Inside HTML (PHP-like style)
For those who prefer the PHP-like style of code inside special tags, Karrigell provides “Python Inside HTML”. The scripts, with the extension pih, are made of HTML code, and of Python code inside the tags <% and %>
Going back to our first example of a script which receives the user name, we could have written this in a script order.pih :
Hello, <% print _user Session().user = _user %> What would you like to order : <form action=”order.py”> CD <input type=”radio” name=”type” value=”CD”> book <input type=”radio” name=”type” value=”book”> <input type=”submit” value=”Ok”> </form>
There are no strict rules for choosing between Python scripts, Karrigell services and Python Inside HTML. I tend to use
- Python Inside HTML for the pages that are mainly static HTML, with only a few places with dynamic content
- Karrigell services for small applications, where creating a file per page would result in too many 10- or 20-line scripts
- Python scripts for the scripts that do more processing of data than sending outpout to the user (typically, the insertion of a new record in a database)
8. Redirection
To perform an HTTP redirection from inside a script, you raise an exception available in the namespace, HTTP_REDIRECTION, with the url as argument :
raise HTTP_REDIRECTION,”index.html”
For instance, if you want to enter a new record in a database, here is a simple ks script :
from HTMLTags import *
def index():
print FORM(TEXT(‘login’)+INPUT(name=”login”)+BR()+TEXT(‘password’)+INPUT(name=”password”)+BR()+INPUT(Type=”submit”),action = “insert”)
def insert(login,password):
(… perform insertion in the database here …)
raise HTTP_REDIRECTION,”index”
After the insertion has been performed, the program will ask another entry