Markdown and web.py Tutorial
February 24, 2012
This is a short tutorial which describes how to build a simple web.py application which serves a directory structure of Markdown-formatted text files as HTML using Python-Markdown.
First, you need to have web.py and Python-Markdown installed. On Debian Linux, this is as simple as typing
sudo apt-get install python-markdown python-webpy
With MacPorts, it’s a little more difficult (see this page for more details):
sudo port install python27 py27-markdown
sudo port select --set python python27
sudo easy_install web.py
The second command above makes the MacPorts version of Python 2.7 the default Python on the system.
Now, the directory structure for the app will be as follows:
webpy-markdown
|- app.py
|- pages
| |- index.txt
| `- other.txt
`- templates
`- page.html
The Python script app.py
contains the application code itself, the
pages
directory contains two Markdown-formatted text files, and the
templates
directory contains a single HTML page template. All of
these files used in the tutorial are available in a tarball:
webpy-markdown.tar.gz.
Let’s start with the app. First, we import the web.py and Python-Markdown modules:
import web
import markdown
Then, as is standard with web.py
apps, we define urls
, mapping
requested URLs to the Python classes which handle them. In this case,
we map everything to the page
class:
# URLs: map everything to the page class
urls = (
'/(.*)', 'page',
)
Next we create the template renderer and application instances,
telling web.py that the templates are stored in the templates/
directory and passing along the URL mapping from above:
# Templates are found in the templates directory
render = web.template.render('templates')
# Application
app = web.application(urls, globals())
Since our app will need to convert Markdown to HTML, we also create an
instance of Python-Markdown called md
:
# Markdown
md = markdown.Markdown(output_format='html4')
Finally, we create the page
class, which will have a single GET
method to handle HTTP GET requests. We must map the given URL to
a text file in the pages/
directory, load that file, convert
the content to HTML, and serve the rendered template. When the
class page:
def GET(self, url):
# Handle index pages: path/ maps to path/index.txt
if url == "" or url.endswith("/"):
url += "index"
# Each URL maps to the corresponding .txt file in pages/
page_file = 'pages/%s.txt' %(url)
# Try to open the text file, returning a 404 upon failure
try:
f = open(page_file, 'r')
except IOError:
return web.notfound()
# Read the entire file, converting Markdown content to HTML
content = f.read()
content = md.convert(content)
# Render the page.html template using the converted content
return render.page(content)
There are a couple of notable things here. First, we handle index
pages in a special way, mapping URLs like /foo/
to /foo/index.txt
.
Second, if the file corresponding to the requested URL cannot be
opened, then we return a 404 error via web.notfound()
. Finally,
the last line renders the template stored in the file named
page.html
.
Lastly, as always, we want to run the application if this file is being invoked as an executable.
if __name__ == '__main__':
app.run()
The template file is very simple, and is listed below. The first line
indicates that the render.<templatename>
function must be called
with a single argument, content
. This is substituted in the body in
place of $:content
(without escaping the HTML, due to the colon).
$def with (content)
<!doctype html>
<html>
<head>
<title>Markdown Website</title>
</head>
<body>
$:content
</body>
</html>
Now, create a file called pages/index.txt
with some Markdown
content, such as the following:
# Markdown Website
This file is `pages/index.txt`, converted to HTML using Pyton-Markdown.
Here is a link to [another page](other).
Finally, to start the server, type the following:
python app.py
Then, open http://localhost:8080/ in your browser and you should see something like the following: