rul4 – Rendering UL4 templates

Purpose

rul4 is a script that can be used to render an UL4 template.

The globals object

Inside the template the object globals (an instance of the class Globals) will be available to make database connections, load and save files, compile templates, access environment variables and parameters etc. However access to those features can be switched off via command line options.

Options

rul4 supports the following options:

templates

One or more template files. A file named - will be treated as standard input. The first file in the list is the main template, i.e. the one that gets rendered. All templates will be available in the main template as the globals.templates dictionary. The keys are the base names of the files (i.e. foo.ul4 will be globals.templates.foo; stdin will be globals.templates.stdin).

--oracle <flag>

Provide the method Globals.oracle() (as globals.oracle) to the template? If switched off globals.oracle will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

--sqlite <flag>

Provide the method Globals.sqlite() (as globals.sqlite) to the template? If switched off globals.sqlite will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

--mysql <flag>

Provide the method Globals.mysql() (as globals.mysql) to the template? If switched off globals.mysql will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

--redis <flag>

Provide the method Globals.redis() (as globals.redis) to the template? If switched off globals.redis will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

--system <flag>

Provide the method Globals.system() (as globals.system) to the template? If switched off globals.system will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

--load <flag>

Provide the method Globals.load() (as globals.load) to the template? If switched off globals.load will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

--save <flag>

Provide the method Globals.save() (as globals.save) to the template? If switched off globals.save will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

--compile <flag>

Provide the method Globals.compile() (as globals.compile) to the template? If switched off globals.compile will be None.

(Allowed values are false, no, 0, true, yes or 1; the default is true)

-e <encoding>, --encoding <encoding>

The encoding of the templates files (default utf-8)

-w <value>, --whitespace <value>

Specifies how to handle whitespace in the template (Allowed values are keep, strip, or smart). This can of course be overwritten with the template tag <?whitespace ...?> in the template files.

-D, --define

Defines an additional variable that will be available inside the template (e.g. the variable foo will be available as globals.vars.foo). -D can be specified multiple times. The following formats are supported:

var

Defines var as an empty string;

var=value

Defines var as the string value;

var:type

Defines var as an empty variable of the type type;

var:type=value

Defines var as a variable of the type type with the value value.

type can be any of the following:

int

value is an integer value.

float

value is a float value.

bool

value is a boolean value. 0, no, false, False or the empty string will be recognized as false and 1, yes, true or True will be recognized as true.

str

value is a string.

oracle

value will be a connection to an Oracle database, e.g.:

-Ddb:oracle=user/password@database
sqlite

value is a connection to an SQLite database.

mysql

value is a connection to a MySQL database.

redis

value will be a connection to an Redis database, e.g.:

-Ddb:redis=192.168.123.1:6379/42

The port (i.e. the 6379 in the above value) is optional and defaults to 6379. The database number (i.e. the 42 in the above value) is also optional and defaults to 0.

Example

This example shows how to connect to an Oracle database and output the content of a person table into an XML file.

Suppose we have a database table that looks like this:

create table person
(
        id integer not null,
        firstname varchar2(200),
        lastname varchar2(200)
);

Then we can use the following template to output the table into an XML file:

<?xml version='1.0' encoding='utf-8'?>
<?code db = globals.oracle("user/password@database")?>
<persons>
        <?for p in db.query("select id, firstname, lastname from person order by 3, 2")?>
                <person id="<?printx p.id?>">
                        <firstname><?printx p.firstname?></firstname>
                        <lastname><?printx p.lastname?></lastname>
                </person>
        <?end for?>
</persons>

If we put the template into the file person.ul4 we can call rul4 like this:

$ rul4 person.ul4 >person.xml

We could also pass the connection to our database via the -D option and disallow the script to make any database connections itself or execute any system commands:

$ rul4 person.ul4 -Ddb:oracle=user/password@database --oracle=0 --sqlite=0 --mysql=0 --redis=0 --system=0 >person.xml

Then the template can use the Oracle connection object db directly.

API

class ll.scripts.rul4.Connection[source]

Bases: object

A Connection object provides a database connection to an UL4 template.

To execute SQL the two methods query() and execute() are provided.

Calling functions or procedures with out parameters can be done with variable objects that can be created with the methods int(), number(), str(), clob() and date(). The resulting value of the out parameter is available from the value attribute of the variable object. The following example creates a function, calls it to get at the result and drops it again:

<?code db = oracle.connect('user/password@database')?>
<?code db.execute('''
        create or replace function ul4test(p_arg integer)
        return integer
        as
        begin
                return 2*p_arg;
        end;
''')?>
<?code vout = db.int()?>
<?code db.execute('begin ', vout, ' := ul4test(42); end;')?>
<?print vout.value?>
<?code db.execute('drop function ul4test')?>

A Connection object can be created with the methods Globals.mysql() or Globals.sqlite().

query(*queryparts)[source]

Execute the query passed in and return an iterator over the resulting records.

At least one positional argument is required. Arguments alternate between fragments of the SQL query and parameters that will be embedded in the query. For example:

<?code db = globals.oracle("user/pwd@db")?>
<?code name = "Bob"?>
<ul>
        <?for p in db.query(
                "select * from person where firstname=",
                name,
                " or lastname=",
                name
        )?>
                <li><?print p.firstname?> <?print p.lastname?></li>
        <?end for?>
</ul>

The records returned from query() are dict-like objects mapping field names to field values.

queryone(*queryparts)[source]

Execute the query passed in and return the first result record (or None if the query didn’t output any record). queryparts is handled the same way as query() does.

execute(*queryparts)[source]

Similar to query() and queryone(), but doesn’t doesn’t return a result. This can be used to call functions or procedures.

str(value=None)[source]

Create a variable that can be used for OUT parameters of type varchar.

clob(value=None)[source]

Create a variable that can be used for OUT parameters of type clob.

int(value=None)[source]

Create a variable that can be used for OUT parameters of type integer.

number(value=None)[source]

Create a variable that can be used for OUT parameters of type number.

date(value=None)[source]

Create a variable that can be used for OUT parameters of type date.

class ll.scripts.rul4.OracleConnection[source]

Bases: Connection

OracleConnection is a subclass of Connection that implements functionality that is specific to Oracle databases (e.g. support for variables). The inferface is the same as Connections.

An OracleConnection object can be created with the method Globals.oracle().

class ll.scripts.rul4.RedisConnection[source]

Bases: object

A connection to a Redis database. A RedisConnection object provides the methods get() to read data from the database and set() to write data to the database.

Example:

<?code db = redis.connect("192.168.123.42/1")?>
<?code value = db.get("key")?>
<?if value is None?>
        <?code value = "foobar"?>
        <?code db.put("key", value, timedelta(seconds=10*60))?>
<?end if?>
get(key)[source]

Return the value for the key key or None if the key doesn’t exist.

set(key, data, timeout=None)[source]

Store the string value data under the key key.

If timeout is None the value will be stored indefinitely. Otherwise it specifies when the value will expire. timeout can be an integer (the number of seconds) or a timedelta object.

class ll.scripts.rul4.Globals[source]

Bases: object

An instance of the Globals class will be passed to the main template as the globals variable. The following attributes will be accessible to UL4 templates:

templatesdictionary

A dictionary containing the templates specified on the command line. This will include the main template.

varsdictionary

A dictionary containing the variables that have been specified via the -D/--define option.

encodingstring

The encoding that will be used for output (this is the same as sys.stdout.encoding, so it can be set with the environment variable PYTHONIOENCODING).

envdictionary

A reference to os.environ.

Furthermore the following methods can be called from UL4 templates: error(), log(), oracle(), mysql(), sqlite(), redis(), system(), load(), save() and compile().

from_args(args)[source]

Sets the attributes of self from the object args (which must be an instance of argparse.Namespace).

Returns the main template.

error(message, ast=None)[source]

Can be called to output an error message and abort template execution. The signature is:

globals.error(message, ast=None)

message is the error message and ast can be an AST node from an UL4 template syntax tree to print an error message that originates from that node.

log(*args, sep=' ', end='\n', flush=False)[source]

Logs args to sys.stderr.

The parameters sep, end and flush have the same meaning as for print().

oracle(connectstring)[source]

Return an OracleConnection object for the Oracle connect string passed in:

<?code db = globals.oracle("user/password@database")?>
<?for row in db.query("select sysdate as sd from dual")?>
        <?print row.sd?>
<?end for?>
mysql(connectstring)[source]

Return a Connection object to a MySQL database for the connectstring passed in. The format of the connect string is:

user/password@host/database
sqlite(connectstring)[source]

Return a Connection object to an SQLite database for the connectstring passed in. The connectstring will be passed directly to sqlite3.connect().

redis(connectstring)[source]

Return a RedisConnection object, which provides a connection to a Redis database. The connectstring has the format:

host:port/db

port is optional and defaults to 6379. db is optional too and defaults to 0.

system(cmd)[source]

Execute the system command cmd and returns its output, e.g. the template:

<?print globals.system("whoami")?>

will output the user name.

load(filename, encoding='utf-8')[source]

Read a file from disk and returns the content. filename is the filename and encoding is the encoding of the file. The encoding parameter is optional and defaults to "utf-8":

<?code data = globals.load("/home/user/data.txt", "iso-8859-1")?>
save(filename, data, encoding='utf-8')[source]

Save the string data to a file on disk. filename is the filename and encoding is the encoding of the file. The encoding parameter is optional and defaults to "utf-8":

<?code globals.save("/home/user/data.txt", "foo\nbar\n", "iso-8859-1")?>
compile(source, name=None, whitespace='keep', signature=None)[source]

Compile the UL4 source source into a Template object and return it. All other parameters are passed to the Template constructor too.