summaryrefslogtreecommitdiff
path: root/docs/users_guide/inheritanceEtc.rst
blob: e0bb12546a80b2e38e941925eaf3ef8e55704027 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
Import, Inheritance, Declaration and Assignment
===============================================

(inheritanceEtc)

#import and #from directives
----------------------------

(inheritanceEtc.import)

Syntax:

::

    #import MODULE_OR_OBJECT [as NAME] [, ...]
    #from MODULE import MODULE_OR_OBJECT [as NAME] [, ...]

The {#import} and {#from} directives are used to make external
Python modules or objects available to placeholders. The syntax is
identical to the import syntax in Python. Imported modules are
visible globally to all methods in the generated Python class.

::

    #import math
    #import math as mathModule
    #from math import sin, cos
    #from math import sin as _sin
    #import random, re
    #from mx import DateTime         # ## Part of Egenix's mx package.

After the above imports, {$math}, {$mathModule}, {$sin}, {$cos} and
{$\_sin}, {$random}, {$re} and {$DateTime} may be used in
{$placeholders} and expressions.

#extends
--------

(inheritanceEtc.extends)

Syntax:

::

    #extends CLASS

All templates are subclasses of {Cheetah.Template.Template}.
However, it's possible for a template to subclass another template
or a pure Python class. This is where {#extends} steps in: it
specifies the parent class. It's equivalent to PSP's
{"@page extends="} directive.

Cheetah imports the class mentioned in an {#extends} directive
automatically if you haven't imported it yet. The implicit
importing works like this:

::

    #extends Superclass
    ## Implicitly does '#from Superclass import Superclass'.

    #extends Cheetah.Templates.SkeletonPage
    ## Implicitly does '#from Cheetah.Templates.SkeletonPage import SkeletonPage'.

If your superclass is in an unusual location or in a module named
differently than the class, you must import it explicitly. There is
no support for extending from a class that is not imported; e.g.,
from a template dynamically created from a string. Since the most
practical way to get a parent template into a module is to
precompile it, all parent templates essentially have to be
precompiled.

There can be only one {#extends} directive in a template and it may
list only one class. In other words, templates don't do multiple
inheritance. This is intentional: it's too hard to initialize
multiple base classes correctly from inside a template. However,
you can do multiple inheritance in your pure Python classes.

If your pure Python class overrides any of the standard {Template}
methods such as {.\_\_init\_\_} or {.awake}, be sure to call the
superclass method in your method or things will break. Examples of
calling the superclass method are in section
tips.callingSuperclassMethods. A list of all superclass methods is
in section tips.allMethods.

In all cases, the root superclass must be {Template}. If your
bottommost class is a template, simply omit the {#extends} in it
and it will automatically inherit from {Template}. { If your
bottommost class is a pure Python class, it must inherit from
{Template} explicitly: }

::

    from Cheetah.Template import Template
    class MyPurePythonClass(Template):

If you're not keen about having your Python classes inherit from
{Template}, create a tiny glue class that inherits both from your
class and from {Template}.

Before giving any examples we'll stress that Cheetah does { not}
dictate how you should structure your inheritance tree. As long as
you follow the rules above, many structures are possible.

Here's an example for a large web site that has not only a general
site template, but also a template for this section of the site,
and then a specific template-servlet for each URL. (This is the
"inheritance approach" discussed in the Webware chapter.) Each
template inherits from a pure Python class that contains
methods/attributes used by the template. We'll begin with the
bottommost superclass and end with the specific template-servlet:

::

    1.  SiteLogic.py (pure Python class containing methods for the site)
            from Cheetah.Template import Template
            class SiteLogic(Template):

    2.  Site.tmpl/py  (template containing the general site framework;
                       this is the template that controls the output,
                       the one that contains "<HTML><HEAD>...", the one
                       that contains text outside any #def/#block.)
            #from SiteLogic import SiteLogic
            #extends SiteLogic
            #implements respond

    3.  SectionLogic.py  (pure Python class with helper code for the section)
            from Site import Site
            class SectionLogic(Site)

    4.  Section.tmpl/py  (template with '#def' overrides etc. for the section)
            #from SectionLogic import SectionLogic
            #extends SectionLogic

    5.  page1Logic.py  (pure Python class with helper code for the template-servlet)
            from Section import Section
            class indexLogic(Section):

    6.  page1.tmpl/py  (template-servlet for a certain page on the site)
            #from page1Logic import page1Logic
            #extends page1Logic

A pure Python classes might also contain methods/attributes that
aren't used by their immediate child template, but are available
for any descendant template to use if it wishes. For instance, the
site template might have attributes for the name and e-mail address
of the site administrator, ready to use as $placeholders in any
template that wants it.

{ Whenever you use {#extends}, you often need {#implements} too,}
as in step 2 above. Read the next section to understand what
{#implements} is and when to use it.

#implements
-----------

(inheritanceEtc.implements)

Syntax:

::

    #implements METHOD

You can call any {#def} or {#block} method directly and get its
outpt. The top-level content - all the text/placeholders/directives
outside any {#def}/{#block} - gets concatenated and wrapped in a
"main method", by default {.respond()}. So if you call
{.respond()}, you get the "whole template output". When Webware
calls {.respond()}, that's what it's doing. And when you do 'print
t' or 'str(t)' on a template instance, you're taking advantage of
the fact that Cheetah makes {.\_\_str\_\_()} an alias for the main
method.

That's all fine and dandy, but what if your application prefers to
call another method name rather than {.respond()}? What if it wants
to call, say, {.send\_output()} instead? That's where {#implements}
steps in. It lets you choose the name for the main method. Just put
this in your template definition:

::

    #implements send_output

When one template extends another, every template in the
inheritance chain has its own main method. To fill the template,
you invoke exactly one of these methods and the others are ignored.
The method you call may be in any of the templates in the
inheritance chain: the base template, the leaf template, or any in
between, depending on how you structure your application. So you
have two problems: (1) calling the right method name, and (2)
preventing an undesired same-name subclass method from overriding
the one you want to call.

Cheetah assumes the method you will call is {.respond()} because
that's what Webware calls. It further assumes the desired main
method is the one in the lowest-level base template, because that
works well with {#block} as described in the Inheritance Approach
for building Webware servlets (section webware.inheritance), which
was originally the principal use for Cheetah. So when you use
{#extends}, Cheetah changes that template's main method to
{.writeBody()} to get it out of the way and prevent it from
overriding the base template's {.respond()}.

Unfortunately this assumption breaks down if the template is used
in other ways. For instance, you may want to use the main method in
the highest-level leaf template, and treat the base template(s) as
merely a library of methods/attributes. In that case, the leaf
template needs {#implements respond} to change its main method name
back to {.respond()} (or whatever your application desires to
call). Likewise, if your main method is in one of the intermediate
templates in an inheritance chain, that template needs {#implements
respond}.

The other way the assumption breaks down is if the main method {
is} in the base template but that template extends a pure Python
class. Cheetah sees the {#extends} and dutifully but incorrectly
renames the method to {.writeBody()}, so you have to use
{#implements respond} to change it back. Otherwise the dummy
{.respond()} in {Cheetah.Template} is found, which outputs...
nothing. { So if you're using {#extends} and get no output, the {
first} thing you should think is,
"Do I need to add {#implements respond} somewhere?" }

#set
----

(inheritanceEtc.set)

Syntax:

::

    #set [global] $var = EXPR

{#set} is used to create and update local variables at run time.
The expression may be any Python expression. Remember to preface
variable names with $ unless they're part of an intermediate result
in a list comprehension.

Here are some examples:

::

    #set $size = $length * 1096
    #set $buffer = $size + 1096
    #set $area = $length * $width
    #set $namesList = ['Moe','Larry','Curly']
    #set $prettyCountry = $country.replace(' ', '&nbsp;')

{#set} variables are useful to assign a short name to a
{$deeply.nested.value}, to a calculation, or to a printable version
of a value. The last example above converts any spaces in the
'country' value into HTML non-breakable-space entities, to ensure
the entire value appears on one line in the browser.

{#set} variables are also useful in {#if} expressions, but remember
that complex logical routines should be coded in Python, not in
Cheetah!

::

    #if $size > 1500
      #set $adj = 'large'
    #else
      #set $adj = 'small'
    #end if

Or Python's one-line equivalent, "A and B or C". Remember that in
this case, B must be a true value (not None, '', 0, [] or {}).

::

    #set $adj = $size > 1500 and 'large' or 'small'

(Note: Cheetah's one-line {#if} will not work for this, since it
produces output rather than setting a variable.

You can also use the augmented assignment operators:

::

    ## Increment $a by 5.
    #set $a += 5

By default, {#set} variables are not visible in method calls or
include files unless you use the {global} attribute: {#set global
$var = EXPRESSION}. Global variables are visible in all methods,
nested templates and included files. Use this feature with care to
prevent surprises.

#del
----

(inheritanceEtc.del)

Syntax:

::

    #del $var

{#del} is the opposite of {#set}. It deletes a { local} variable.
Its usage is just like Python's {del} statement:

::

    #del $myVar
    #del $myVar, $myArray[5]

Only local variables can be deleted. There is no directive to
delete a {#set global} variable, a searchList variable, or any
other type of variable.

#attr
-----

(inheritanceEtc.attr)

Syntax:

::

    #attr $var = EXPR

The {#attr} directive creates class attributes in the generated
Python class. It should be used to assign simple Python literals
such as numbers or strings. In particular, the expression must {
not} depend on searchList values or {#set} variables since those
are not known at compile time.

::

    #attr $title = "Rob Roy"
    #attr $author = "Sir Walter Scott"
    #attr $version = 123.4

This template or any child template can output the value thus:

::

    $title, by $author, version $version

If you have a library of templates derived from etexts
(http://www.gutenberg.org/), you can extract the titles and authors
and put them in a database (assuming the templates have been
compiled into .py template modules):

#def
----

(inheritanceEtc.def)

Syntax:

::

    #def METHOD[(ARGUMENTS)]
    #end def

Or the one-line variation:

::

    #def METHOD[(ARGUMENTS)] : TEXT_AND_PLACEHOLDERS

The {#def} directive is used to define new methods in the generated
Python class, or to override superclass methods. It is analogous to
Python's {def} statement. The directive is silent, meaning it does
not itself produce any output. However, the content of the method
will be inserted into the output (and the directives executed)
whenever the method is later called by a $placeholder.

::

    #def myMeth()
    This is the text in my method
    $a $b $c(123)  ## these placeholder names have been defined elsewhere
    #end def

    ## and now use it...
    $myMeth()

The arglist and parentheses can be omitted:

::

    #def myMeth
    This is the text in my method
    $a $b $c(123)
    #end def

    ## and now use it...
    $myMeth

Methods can have arguments and have defaults for those arguments,
just like in Python. Remember the {$} before variable names:

::

    #def myMeth($a, $b=1234)
    This is the text in my method
    $a - $b
    #end def

    ## and now use it...
    $myMeth(1)

The output from this last example will be:

::

    This is the text in my method
    1 - 1234

There is also a single line version of the {#def} directive. {
Unlike the multi-line directives, it uses a colon (:) to delimit
the method signature and body}:

::

    #attr $adj = 'trivial'
    #def myMeth: This is the $adj method
    $myMeth

Leading and trailing whitespace is stripped from the method. This
is in contrast to:

::

    #def myMeth2
    This is the $adj method
    #end def

where the method includes a newline after "method". If you don't
want the newline, add {#slurp}:

::

    #def myMeth3
    This is the $adj method#slurp
    #end def

Because {#def} is handled at compile time, it can appear above or
below the placeholders that call it. And if a superclass
placeholder calls a method that's overridden in a subclass, it's
the subclass method that will be called.

#block ... #end block
---------------------

(inheritanceEtc.block)

The {#block} directive allows you to mark a section of your
template that can be selectively reimplemented in a subclass. It is
very useful for changing part of a template without having to
copy-paste-and-edit the entire thing. The output from a template
definition that uses blocks will be identical to the output from
the same template with the {#block ... #end block} tags removed.

({ Note:} don't be confused by the generic word 'block'' in this
Guide, which means a section of code inside { any} {#TAG ... #end
TAG} pair. Thus, an if-block, for-block, def-block, block-block
etc. In this section we are talking only of block-blocks.)

To reimplement the block, use the {#def} directive. The magical
effect is that it appears to go back and change the output text {
at the point the original block was defined} rather than at the
location of the reimplementation.

::

    #block testBlock
    Text in the contents
    area of the block directive
    #if $testIt
    $getFoo()
    #end if
    #end block testBlock

You can repeat the block name in the {#end block} directive or not,
as you wish.

{#block} directives can be nested to any depth.

::

    #block outerBlock
    Outer block contents

    #block innerBlock1
    inner block1 contents
    #end block innerBlock1

    #block innerBlock2
    inner block2 contents
    #end block innerBlock2

    #end block outerBlock

Note that the name of the block is optional for the {#end block}
tag.

Technically, {#block} directive is equivalent to a {#def} directive
followed immediately by a {#placeholder} for the same name. In
fact, that's what Cheetah does. Which means you can use
{$theBlockName} elsewhere in the template to output the block
content again.

There is a one-line {#block} syntax analagous to the one-line
{#def}.

The block must not require arguments because the implicit
placeholder that's generated will call the block without
arguments.