Code Execution
There are several ways to get code to execute during rendering of a Twiddler. The simplest example of this is to include a code block in your Twiddler's source and use the appropriate parser.
It's important to note, however, that only the first code block found will be executed. All other code blocks will be removed without being executed. All other comments will be ignored and left in place. This can all be seen in the example below:
>>> from twiddler import Twiddler
>>> from twiddler.input.default import DefaultWithCodeBlock
>>> t = Twiddler('''<html>
... <!--twiddler
... def manipulate(t):
... t['number'].replace('One',name=False)
... -->
... <body>
... <!-- The above code will process the following: -->
... <div name="row">This is row <i name="number">1</i></div>
... <!--twiddler
... def nope(t):
... t['number2'].replace('Two',name=False)
... -->
... <div name="row2">This is row <i name="number2">1</i></div>
... <!-- However, the above code will note get executed -->
... </body>
... </html>''',input=DefaultWithCodeBlock)
>>> print t.render()
<html>
<body>
<!-- The above code will process the following: -->
<div name="row">This is row <i>One</i></div>
<div name="row2">This is row <i name="number2">1</i></div>
<!-- However, the above code will note get executed -->
</body>
</html>
You can also pass in another source of code execution by way of the 'executor' parameter to the Twiddler constructor. This must be a callable object:
def do_stuff(t): ... t['stuff'].replace('Stuff done!')
t = Twiddler('',executor=do_stuff) print t.render() Stuff done!
However, executors must all be pickleable. This can be tricky if your executable comes from a piece of text, such as in the DefaultWithCodeBlock example above.
If this is the case, you can use the Source executor. This takes a piece of text containing the definition of a function as its source. This function will be used as the executor. Here's a simple example:
source = ''' ... def do_stuff(t): ... t['stuff'].replace('Stuff done from source!') ... '''
from twiddler.executor.source import Source t = Twiddler('',executor=Source(source)) print t.render() Stuff done from source!
In the examples so far, the code called during execution hasn't returned any value. If a value is returned, it must be a Twiddler, and it is the output renderer of the returned Twiddler that is used to render the resulting output that is returned from the render method.
To demonstrate this, lets first create a generic Twiddler used for rendering emails in a particular application:
from twiddler.input.plaintext import PlainText
from twiddler.output.emailer import Email,DummySMTP
template = Twiddler('''Dear Customer, ...$body''',
input=PlainText, output=Email(smtp_host=DummySMTP, ...
mfrom='webmaster@example.com', ... subject='Some Email!', ...
mto='user@example.com'))
Now, lets create a Twiddler used for displaying orders. You'll notice that this Twiddler uses the default output:
order = Twiddler(''' ... $description $price ... ''',input=PlainText)
Now we need an executor to render the order and turn it into an email:
def email_order(ot,order): ... ti = ot['item'].repeater() ... for item in order: ... i = ti.repeat() ... i['description'].replace(item['description']) ... i['price'].replace(item['price']) ... t = template.clone() ... t['body'].replace(ot['order']) ... return t
The important point to note about this executor is that the Twiddler returned is a modified version of the template, rather than the Twiddler that is passed in.
To use the executor, we assign it to our order Twiddler:
order.executor = email_order
Now, rendering an order with some data will result in an email being sent by way of the template's Email output:
order.render(( ... {'description':'Item X','price':20.01}, ...
{'description':'Item Y','price':19.40}, ... )) Dummy SMTP send
from 'webmaster@example.com' to 'user@example.com' Content-Type:
text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding:
quoted-printable Date: ... From: webmaster@example.com Message-ID: ...
Subject: Some Email! To: user@example.com Dear Customer, Item X 20.01 Item Y 19.4
You may have noticed that parameters passed to the render method are passed through to the executor. In effect, the function signature for the render method will match that of any executor, except that the executor will additionally have the Twiddler being rendered passed as the first positional parameter.
Here's an example to demonstrate this, by rendering some simple contact details:
>>> person = {
... 'name':'Chris',
... 'address':'7 Wysteria Lane',
... 'phone':'1-800-CALLME',
... }
Our Twiddler this time has a code block and uses the DefaultWithCodeblock input:
>>> t = Twiddler('''<html>
... <!--twiddler
... def substitute(t,person,colour='red'):
... t['name'].replace(person['name'],style="color:"+colour)
... t['address'].replace(person['address'])
... t['phone'].replace(person['phone'])
... -->
... <body>
... <table>
... <tr>
... <th>Name</th>
... <td id="name"></td>
... </tr>
... <tr>
... <th>Address</th>
... <td id="address"></td>
... </tr>
... <tr>
... <th>Telephone Number</th>
... <td id="phone"></td>
... </tr>
... </table>
... </body>
... </html>''',input=DefaultWithCodeBlock)
We now take a copy of it and render it, which results in the code block being executed and passed the parameters we pass to the render method:
>>> t2 = t.clone()
>>> print t2.render(person)
<html>
<body>
<table>
<tr>
<th>Name</th>
<td id="name" style="color:red">Chris</td>
</tr>
<tr>
<th>Address</th>
<td id="address">7 Wysteria Lane</td>
</tr>
<tr>
<th>Telephone Number</th>
<td id="phone">1-800-CALLME</td>
</tr>
</table>
</body>
</html>
One final thing to note is that any executor will only be used once. To show this, lets have a simpler counter class and an instance of that class:
class Counter: ... count = 0 counter = Counter()
Now a simple executor that uses it:
def do_stuff(t,counter): ... counter.count += 1 ... t['item'].replace(counter.count)
Now we can see it in action:
tx = Twiddler('',executor=do_stuff) print tx.render(counter) 1
We can see that the executor is only used once by rendering the Twiddler again and noting that the number has not been incremented:
print tx.render(counter) 1
If we re-instate the executor, the number will be incremented:
tx.executor = do_stuff print tx.render(counter) 2
This may at first seem counter-intuitive, but it's important to remember that most executors will alter the Twiddler in such a way that they will fail to run successfully a second time:
def ebad(t): t['test'].replace('hello!',tag=False,id=False) tbad = Twiddler('',executor=ebad) print tbad.render() hello! tbad.executor = ebad print tbad.render() Traceback (most recent call last):
KeyError:...
For this reason, always clone a Twiddler with an executor prior to rendering it:
counter.count = 0 src = ''' ... def do_stuff(t,counter): ... global count ... counter.count += 1 ... t['item'].replace(counter.count,id=False) ... ''' tx = Twiddler('',executor=Source(src,name='do_stuff')) print tx.clone().render(counter) 1 print tx.clone().render(counter) 2 print tx.clone().render(counter) 3
Execute Method
In addition to the render method, Twiddlers also have an execute method. This invokes any executor that is present without actually rendering the Twiddler. The parameters to the execute method are passed through to the executor in exactly the same way as for the render method.
Here's an example using a copy of Twiddler from the contact details example above:
>>> t3 = t.clone()
>>> t3.execute(person,'blue')
<twiddler.Twiddler instance at ...>
As you can see, a Twiddler instance is returned. This is either the return value of the executor or, if there is no executor or the executor does not return anything, it will be the Twiddler instance on which execute was called.
We can now call the render method and we don't have to pass any parameters as the code block has already been executed:
>>> print t3.render()
<html>
<body>
<table>
<tr>
<th>Name</th>
<td id="name" style="color:blue">Chris</td>
</tr>
<tr>
<th>Address</th>
<td id="address">7 Wysteria Lane</td>
</tr>
<tr>
<th>Telephone Number</th>
<td id="phone">1-800-CALLME</td>
</tr>
</table>
</body>
</html>
We can also call the execute method again and nothing will happen this time, because the code block in the template has already been executed:
>>> t = t3.execute()
>>> print t.render()
<html>
<body>
<table>
<tr>
<th>Name</th>
<td id="name" style="color:blue">Chris</td>
</tr>
<tr>
<th>Address</th>
<td id="address">7 Wysteria Lane</td>
</tr>
<tr>
<th>Telephone Number</th>
<td id="phone">1-800-CALLME</td>
</tr>
</table>
</body>
</html>
The execute method is particular useful when rendering output from a combination of a site template and an specific Twiddler.
For example, say we have a site template with a code block:
>>> template = Twiddler('''<html>
... <!--twiddler
... def substitute(t,title):
... t['title'].replace(title,id=False)
... r = t['menu_item'].repeater()
... r.repeat('item 1',name=False)
... r.repeat('item 2',name=False)
... r.repeat('item 3',name=False)
... -->
... <head><title id="title" /></head>
... <body>
... <div id="navigation">
... <ul>
... <li name="menu_item" />
... </ul>
... </div>
... <div id="body">
... </div>
... </body>
... </html>''',input=DefaultWithCodeBlock)
And now we have a particular page that renders itself:
>>> page = Twiddler('''<html>
... <!--twiddler
... def render(p,template):
... t = template.clone()
... t.execute(title='something')
... t['body'].replace(p['body'])
... return t
... -->
... <body>
... <div id="body">
... Our Body!
... </div>
... </body>
... </html>''',input=DefaultWithCodeBlock)
When the page is rendered, its executor is called, which in turn clones and calls the execute method of the template, giving us the following output:
>>> print page.render(template)
<html>
<head><title>something</title></head>
<body>
<div id="navigation">
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
</div>
<div id="body">
Our Body!
</div>
</body>
</html>
The setSource Method
Python frameworks which have persistent object stores, such as Zope, often want to modify the source of an existing Twiddler. For this reason, Twiddlers have a setSource method that parses the supplied source with the Twiddler's input parser.
However, it's important to note that input parsers may return an executor. If they do, the executor returned from parsing the new source will replace any existing executor. If the input parser does not return an executor, then any existing executor will be left in place.
Here's an example, starting with a Twiddler that uses a code block:
>>> t = Twiddler('''<root><node id="test"/>
... <!--twiddler
... def substitute(t,text):
... t['test'].replace(text)
... -->
... </root>''',input=DefaultWithCodeBlock)
We can see the output if we render a copy:
>>> print t.clone().render('Some text')
<root><node id="test">Some text</node>
</root>
We can also see the original has an executor:
>>> print t.executor
<twiddler.executor.source.Source instance at ...>
If we now change the input to be one which returns no executor and then use the setSource method, we can see that the old executor stays in place:
>>> from twiddler.input.plaintext import PlainText
>>> t.input = PlainText
>>> t.setSource('root:$test')
Now, when we can render on a new copy, the old executor will still get used:
>>> print t.clone().render('Some text')
root:Some text
However, inputs which can return an executor, like the default code block input, will most likely make sure there is no executor to avoid a change in source resulting in an old executor getting used with new source in ways that result in errors.
We can see this by switching back to the default code block input parser and then using the setSource method:
>>> t.input = DefaultWithCodeBlock
>>> t.setSource('<node id="test"/>')
There's now no executor:
>>> print t.executor
None
We can also render the Twiddler without passing in any parameters:
>>> print t.render()
<node id="test" />