Uriel allows you to optionally implement four handler functions, that are called at specific times every time you build your web site.
These handler functions are defined in the lib/handlers.py file in your project.
When Uriel first creates a new project, the contents of the lib/handlers.py look like this:
############################################################################## # handlers.py # ############################################################################## # The following symbols are imported using magic: # # import uriel # from uriel import Page # from uriel import Node # from uriel import FileNode # from uriel import VirtualNode # from uriel import HandlerError # from uriel import log # from uriel import escape #def init(project_root): # pass #def before_render_node_tree(project_root, root_node): # pass #def after_render_node_tree(project_root, root_node): # pass #def cleanup(project_root, root_node): # pass
Notice how all of the functions are commented out? If a handler function doesn't exist, then Uriel will not call it.
To enable a handler function, uncomment it in lib/handlers.py and add your own code.
See the Uriel API Reference for more information about what is available to reference inside handler functions.
When Uriel builds your web site, it performs the following steps, in order. Handler function steps are highlighted in bold.
| Step | Comments |
|---|---|
| create_project_root | When uriel is run against a directory name that does not exist, it will create the initial project root directory. |
| init_project_root | Creates initial files and directories in the project root. Restores the public directory to match the contents of the static directory, deleting any previously generated files in the process. |
| init_modules | Import the lib/soju.py and lib/handlers.py modules. |
| init() | Calls the init(project_root) function in lib/handlers.py |
| create_file_node_tree | Read the node files and turn them into an in-memory tree of Node object instances. |
| before_render_node_tree() | Calls the before_render_node_tree(project_root, root_node) function in lib/handlers.py |
| augment_node_tree | Augments Node object instances with additional values before rendering. |
| render_node_tree | Renders nodes and templates together in memory, in preparation for writing the HTML files into the public directory. |
| after_render_node_tree() | Calls the after_render_node_tree(project_root, root_node) function in lib/handlers.py. This is especially useful if you need to make any changes to the Node objects in memory after the HTML page rendering, but before the RSS feed rendering. |
| write_dynamic_nodes | Writes the rendered nodes out to HTML files in the public directory. |
| write_additional_files | Write additional generated files (RSS, Sitemap, robots.txt). If an RSS feed is generated, this step involves rendering some nodes a second time for the RSS feed. |
| copy_static_files | The contents of the static directory are copied into the public directory. |
| cleanup() | Calls the cleanup(project_root, root_node) function in lib/handlers.py |
On this documentation site, we have two handler functions defined. Here are the relevant functions from lib/handlers.py on this project:
def before_render_node_tree(project_root, root_node):
# find the node for the Handlers page
# (the nodes/handlers file)
example_root = root_node.find_node_by_path("handlers")
# create a new virtual node under the Handers page, with the node path
# "handlers/virtual", as a child of the "handlers" node
vnode = VirtualNode(project_root,
example_root.get_path() + "/virtual",
example_root)
# add the VirtualNode to the list of child nodes for the parent
# "handlers" node
example_root.add_child(vnode)
# N.B. we need to add the parent node in the VirtualNode constructor, and
# also call the add_child() method on the parent node, so that the
# parent/child relationship is established in both directions
# set the Title and RSS-Include headers for this vnode
#
# uriel canonicalizes all header names to lowercase internally, so we
# set headers in all lowercase when creating vnodes
vnode.set_header("title", "VirtualNode Example")
vnode.set_header("rss-include", "true")
# set the node body contents
vnode.set_body(
"<p>Hello from a VirtualNode.</p>\n" +
"\n" +
"<p>See the {{node-link:handlers}} page for more details.</p>"
)
def after_render_node_tree(project_root, root_node):
# find the node for the virtual node we created earlier in the
# before_render_node_tree() handler function
vnode = root_node.find_node_by_path("handlers/virtual")
# get the node body
body = vnode.get_body()
# modify our local copy of the node body contents
body += "\n\n<p>This is a special message for RSS readers only.</p>"
# set the node body to our modified string
vnode.set_body(body)
The code above generates a new virtual node in its before_render_node_tree() implementation.
The after_render_node_tree() implementation also modifies the same virtual node, to change the node body contents before the RSS feed is generated.
You can see the results below:
| Generated Page | RSS Feed |
|---|---|
| VirtualNode Example | https://documentation.uriel.foo/rss.xml |
Tags: user-defined-python-code
This page was generated by Uriel with the following settings:
Page Details
| Resource | Path | Project File |
|---|---|---|
| Node | handlers | nodes/handlers |
| Template | default.html | templates/default.html |
| URL | /handlers/ | public/handlers/index.html |
Node Headers
| Header (Lowercase) | Value |
|---|---|
| tags | user-defined-python-code |
| title | Handlers |
| breadcrumb-separator | » |
| canonical-url | https://documentation.uriel.foo |
| rss-description | Uriel Documentation |
| rss-image-height | 32 |
| rss-image-url | /favicon-32x32.png |
| rss-image-width | 32 |
| rss-max-entries | 50 |
| rss-title | Uriel Documentation |
| rss-url | /rss.xml |
| sitemap-max-entries | 10000 |
| sitemap-url | /sitemap.xml |
| tag-node | tag |
| template | default.html |
Node Timestamps
| Type | Value |
|---|---|
| Created | |
| Modified | 2026-06-10T23:25:17-04:00 |
Node Methods
| Method | Value |
|---|---|
| get_parent_node() | index |
| get_path() | handlers |
| get_node_type() | file |
| get_url() | /handlers/ |
| get_canonical_url() | https://documentation.uriel.foo/handlers/ |
| get_name() | handlers |
| get_display_name() | Handlers |
| get_title() | Handlers |
| get_escaped_title() | Handlers |
| get_link() | <a href="/handlers/">Handlers</a> |
| get_canonical_link() | <a href="https://documentation.uriel.foo/handlers/">Handlers</a> |
| get_link_prefix() | <p> |
| get_link_suffix() | </p> |
| get_tags() | ['user-defined-python-code'] |
| get_dest_dir() | ./public/handlers |
| get_dest_file() | ./public/handlers/index.html |
| get_breadcrumb_separator() | » |