Newer
Older
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
3315
3316
3317
3318
3319
3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
3369
3370
3371
3372
3373
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451
3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
3738
3739
3740
3741
3742
3743
3744
3745
3746
3747
3748
3749
3750
3751
3752
3753
3754
3755
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
3846
3847
3848
3849
3850
3851
3852
3853
3854
3855
3856
3857
3858
3859
3860
3861
3862
3863
3864
3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
3975
3976
3977
3978
3979
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
Below we contrast the difference between static and dynamic includes. A
dynamic include is included on each request; a static include is inserted at
compile time. A static include runs in the same context, while a dynamic
include has a separate context.<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/include.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><font color="#CC00CC">[[.import name=include]]</font>
<html><body>
main file<br>
<hr>
<font color="#0000CC">[[
context = {'foo': 'old value'}
result=include.spyce('include.spi', context)
]]</font>
<hr>
main file again<br>
context: <font color="#CC0000">[[=context]]</font><br>
return value: <font color="#CC0000">[[=result]]</font><br>
</body></html>
</b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/include.spy">Run this code</a></b>
</font>
</td></tr></table>
<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/include.spi</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b>begin include<br>
context: <font color="#CC0000">[[=include.context ]]</font><br>
from: <font color="#CC0000">[[=request.stack()[-2] ]]</font><br>
foo was <font color="#CC0000">[[=include.vars.foo]]</font><br>
setting foo to 'new value' <font color="#0000CC">[[include.vars.foo = 'new value']]</font><br>
returing 'retval'<br>
end include<br>
<font color="#0000CC">[[ return 'retval' ]]</font>
</b></font></pre>
</font>
</td></tr></table>
<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/includestatic.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><html><body>
<font color="#0000CC">[[x=1]]</font>
main file<br>
x=<font color="#CC0000">[[=x]]</font><br>
<hr>
<font color="#CC00CC">[[.include file="includestatic.spi"]]</font>
<hr>
main file again<br>
x=<font color="#CC0000">[[=x]]</font>
</body></html>
</b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/includestatic.spy">Run this code</a></b>
</font>
</td></tr></table>
<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/includestatic.spi</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b>begin included file<br>
changing value of x<br>
<font color="#0000CC">[[x=2]]</font>
end included file<br>
</b></font></pre>
</font>
</td></tr></table>
<p>
<big><a name="mod_internal"></a><b>3.8.11. <font color=#ee0000><i>Internal modules</i></font></b></big><p>
These modules are used internally by Spce. Documentation is included for
those curious about Spyce internals; ordinarily you will never use these
modules directly.
The error module is implicitly loaded and provides error-handling
functionality. An error is any <a
href="#runtime_except">unhandled runtime exception</a> that
occurs <b>during Spyce processing</b>. This mechanism does not include
exceptions that are not related to Spyce processing (i.e. server-related
exceptions), that can be caused before or after Spyce processing by invalid
syntax, missing files and file access restrictions. To install a server-level
error handler use a <a href="#runtime_common">configuration
file</a>. The default page-level error handler can also be modified in the <a
href="#runtime_common">configuration file</a>. This module
allows the user to install page-level error handling code, overriding the
default page-level handler, by using one of the following functions: <p>
<ul>
<li><b>setStringHandler</b>( string ): <br> Installs a function that will
processes the given <b>string</b>, as Spyce code, for error handling.
</li><p>
<li><b>setFileHandler</b>( uri ): <br> Installs a function that will
processes the given <b>uri</b> for error handling. </li><p>
<li><b>setHandler</b>( fn ): <br> Installs the <b>fn</b> function for error
handling. The function is passed one parameter, a reference to the error
module. From this, all the error information as well as references to other
modules and Spyce objects can be accessed.</li><p>
</ul>
The error module provides the following information about an error: <p>
<ul>
<li><b>isError</b>(): <br> Returns whether an error is being handled.
</li><p>
<li><b>getMessage</b>(): <br> Return the error message; the string of the
object that was raised, or None if there is no current error. </li><p>
<li><b>getType</b>(): <br> Return the error type; the type of the object
that was raised, or None if there is no current error. </li><p>
<li><b>getFile</b>(): <br> Return the file where the error was raised, or
None if there is no current error. </li><p>
<li><b>getTraceback</b>(): <br> Return the stack trace as an array of
tuples, or None if there is no current error. Each tuple entry is of the
form: (file, line numbers, function name, code context).</li><p>
<li><b>getString</b>(): <br> Return the string of the entire error (the
string representation of the message, type, location and stack trace), or
None if there is no current error. </li><p>
</ul>
The default error handling function uses the following string handler:
<table border=0 align=center><tr><td>
<pre><font color="#000000"><b>
<font color="#CC00CC">[[.module name=transform]]</font>
<font color="#0000CC">[[transform.expr('html_encode')]]</font>
<html>
<title>Spyce exception: <font color="#CC0000">[[=error.getMessage()]]</font></title>
<body>
<table cellspacing=10 border=0>
<tr><td colspan=2><h1>Spyce exception</h1></td></tr>
<tr><td valign=top align=right><b>File:</b></td><td><font color="#CC0000">[[=error.getFile()]]</font></tr>
<tr><td valign=top align=right><b>Message:</b></td>
<td><pre><font color="#CC0000">[[=error.getMessage()]]</font></pre></tr>
<tr><td valign=top align=right><b>Stack:</b></td><td>
<font color="#0000CC">[[\
L = list(error.getTraceback())
L.reverse()
]]</font>
<font color="#0000CC">[[ for frame in L: { ]]</font>
<font color="#CC0000">[[=frame[0] ]]</font>:<font color="#CC0000">[[=frame[1] ]]</font>, in <font color="#CC0000">[[=frame[2] ]]</font>:<br>
<table border=0><tr><td width=10></td><td>
<pre><font color="#CC0000">[[=frame[3] ]]</font></pre>
</td></tr></table>
<font color="#0000CC">[[ } ]]</font>
</td></tr>
</table>
</body></html>
</b></font></pre>
</td></tr></table>
<p>
The example below shows the error module in use. Error handling can often be
used to send emails notifying webmasters of problems, as this example shows.
<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/error.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><font color="#0000CC">[[error.setFileHandler('error.spi') ]]</font>
This is a page with an error...
<font color="#0000CC">[[ raise 'an error' ]]</font>
</b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/error.spy">Run this code</a></b>
</font>
</td></tr></table>
<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/error.spi</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><h1>Oops</h1>
An error occurred while processing your request.
We have logged this for our webmasters, and they
will fix it shortly. We apologize for the inconvenience.
In the meantime, please use the parts of our site that
actually do work... <a href="somewhere">somewhere</a>.
<font color="#0000CC">[[\
# could redirect the user immediately
#response.getModule('redirect').external('somewhere.spy')
# could send an email
import time
msg = '''
time: %s
error: %s
env: %s
other info...
''' % (
time.asctime(time.localtime(time.time())),
error.getString(),
request.env()
)
#function_to_send_email('webmaster@foo.com', msg)
#or perform other generic error handling...
]]</font>
</b></font></pre>
</font>
</td></tr></table>
<p>
This mechanism is not a subsititute for proper exception handling within the
code itself, and should not be abused. It does, however, serve as a useful
catch-all for bugs that slip through the cracks. <p>
The stdout module is loaded implicitly and redirects Python's <font
face=courier>sys.stdout</font> (in a thread-safe manner) to the appropriate
response object for the duration of Spyce processing. This allows one to use
<font face=courier>print</font>, without having to write <font
face=courier>print >> response, ...</font>. The stdout
module provides a variable <font face=courier>stdout.stdout</font>, which
refers to the original stream, but is unlikely to be needed. It may also be
useful to know that <font face=courier>sys.stderr</font> is, under many
configurations, connected to the webserver error log. <p>
In addition, the stdout module provides the following functions for capturing
or redirecting output:
<ul>
<li><b>push</b>( [filename] ): <br> Begin capturing output. Namely, the current
output stream is pushed onto the stack and replaced with a memory buffer. An
optional <b>filename</b> may be associated with this operation (see pop()
method below). </li> <p>
<li><b>pop</b>(): <br> Close current output buffer, and return the captured
output as a string. If a filename was associated with the push(), then the
string will also be written to that file. </li> <p>
<li><b>capture</b>(f, [*args], [**kwargs] ): <br> Push the current stream,
call the given function <b>f</b> with any supplied arguments <b>*args</b>
and keyword arguments <b>**kwargs</b>, and then pop it back. Capture returns
a tuple (r,s), where r is the result returned by f and s is a string of its
output. </li> <p>
</ul> <p>
The example below show how the module is used:
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/stdout.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><html><body>
<font color="#0000CC">[[ print '''Using the stdout module redirects
stdout to the response object, so you can use
<b>print</b>!''']]</font><br>
redirecting stdout can be used to...
<font color="#0000CC">[[stdout.push()]]</font>
<font color="#0000CC">[[print 'capture']]</font> out<font color="#CC0000">[[='put']]</font>
<font color="#0000CC">[[cached = stdout.pop()]]</font>
... for later: <br>
<font color="#CC0000">[[=cached]]</font>
</body></html>
</b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/stdout.spy">Run this code</a></b>
</font>
</td></tr></table>
This module is used internally by Spyce, not usually by users. Documentation is
included for those curious about Spyce internals. The spylambda module is
loaded implicitly and allows the definition of functions based on Spyce
scripts; see <a href="#lang_lambda">Spyce Lambdas</a>. The
spylambda module provides the following methods:
<ul>
<li><b>define</b>( args, code, [memoize] ): <br> Returns a function that
accepts the given <b>args</b> and executes the Spyce script defined by the
<b>code</b> parameter. Note that the code is compiled immediately and that
<font face=courier>spyce.spyceSyntaxError</font> or <font
face=courier>spyce.spycePythonError</font> exceptions can be thrown for
invalid code arguments. The optional <b>memoize</b> parameter sets whether
the spyce can or can not be memoized, with the default being false.
Memoizing a function means capturing the result and output and caching them,
keyed on the function parameters. Later, if a function is called again with
the same parameters, the cached information is returned, if it exists, and
the function may not actually be called. Thus, you should only memoize
functions that are truly functional, i.e. they do not have side-effects:
they only return a value and output data to the response object, and their
behaviour depends exclusively on their parameters. If you memoize code that
does have side-effects, those side-effects may not occur on every
invocation. </li> <p>
<li><b>__call__</b>( args, code, _spyceCache ): <br> This is an alias to the
define function. Because of the special method name, the spylambda module
object can be <a
href="http://www.python.org/doc/current/ref/callable-types.html">called as
if it were a function</a>. </li> <p>
</ul>
The taglib module is loaded implicitly and supports
<a href="#tag">Active Tags</a>
functionality. The taglib module provides the following
methods:
<ul>
<li><b>load</b>( libname, [libfrom], [libas] ): <br> Loads a tag library
class named <b>libname</b> from a file called <b>libfrom</b> in the search
path, and installed it under the tag prefix <b>libas</b>. The default for
libfrom is <font face=courier><i>libname</i>.py</font>. The default for
libas is <font face=courier><i>libname</i></font>. Once installed, a library
name is its unique tag prefix. </li> <p>
<li><b>unload</b>( libname ): <br> Unload a tag library that is installed
under the <b>libname</b> prefix. This is usually performed only at the end
of a request. </li> <p>
<li><b>tagPush</b>( libname, tagname, pair ): <br> Push a new tag object for
a <b>libname</b>:<b>tagname</b> tag onto the tag stack. The <b>pair</b>
parameter is a flag indicating whether this is a singleton or a paired tag.
</li> <p>
<li><b>tagPop</b>(): <br> Pop the current tag from the tag stack. </li> <p>
<li><b>getTag</b>(): <br> Return the current tag object. </li> <p>
<li><b>outPush</b>(): <br> Begin capturing the current output stream. This
is usually called by the tagBegin method. </li> <p>
<li><b>outPopCond</b>(): <br> End capturing the current output stream, and
return the captured contents. It will only "pop" once, even if called
multiple times for the same tag. This method is usually called by either the
tagEnd(), tagCatch, or tagPop() methods. </li> <p>
<li><b>tagBegin</b>( attrs ): <br> This method sets the tag output and
variable environment, and then calls the tag's <b>begin()</b> method with
the given <b>attrs</b> tag attribute dictionary. This method returns a flag,
and the tag body must be processed if and only if this flag is true. </li>
<p>
<li><b>tagBody</b>(): <br> This method sets the tag output and variable
environment, and then calls the tag's <b>body()</b> method with the captured
output of the body processing. If this method returns true, then the
processing of the body must be repeated. </li> <p>
<li><b>tagEnd</b>(): <br> This method sets the tag output and variable
environment, and then calls the tag's <b>end()</b> method. This method must
be called if the tagBegin() method completes successfully in order to
preserve tag semantics. </li> <p>
<li><b>tagCatch</b>(): <br> This method should be called if any of the
tagBegin, tagBody or tagEnd methods raise an exception. It calls the tag's
<b>catch()</b> method with the current exception. </li> <p>
</ul>
<big><a name="mod_new"></a><b>3.8.12. <font color=#ee0000><i>Writing Modules</i></font></b></big><p>
Writing your own Spyce modules is simple.
<p>
A Spyce modules is simply a Python class that exposes specific methods
to the Spyce server. The most important are <b>start</b>, <b>finish</b>,
and <b>init</b>. With these,
a Spyce module may access the
internal request and response structures or alter the behaviour of the
runtime engine in some way.
<p>Let us begin with a basic example
called myModule. It is a module that implements one function named foo().
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/myModule.py</b></font>
</td></tr><tr><td>
<font face=courier>
<style>
.STRING {
color: #a0522d;
}
.COMMENT {
color: #ff7448;
font-family: serif;
}
.KEYWORD {
color: #9e79af;
}
.FUNCTION {
color: #0074ef;
}
</style>
<pre>
<span class="KEYWORD">from</span> <span class="NAME">spyceModule</span> <span class="KEYWORD">import</span> <span class="NAME">spyceModule</span><span class="NEWLINE">
</span><span class="NL">
</span><span class="KEYWORD">class</span> <span class="FUNCTION">myModule</span><span class="OP">(</span><span class="NAME">spyceModule</span><span class="OP">)</span><span class="OP">:</span><span class="NEWLINE">
</span><span class="INDENT"> </span><span class="KEYWORD">def</span> <span class="FUNCTION">foo</span><span class="OP">(</span><span class="NAME">self</span><span class="OP">)</span><span class="OP">:</span><span class="NEWLINE">
</span><span class="INDENT"> </span><span class="KEYWORD">print</span> <span class="STRING">'foo called'</span><span class="NEWLINE">
</span><span class="NL">
</span><span class="DEDENT"></span><span class="DEDENT"></span><span class="ENDMARKER"></span>
</pre>
</font>
</td></tr></table>
<p>
Saving this code in <font face=courier>myModule.py</font> in the same
directory as the Spyce script, or somewhere on the module path, we could use
it as expected: <p>
<table align=center border=0><tr><td><font face=courier>
<b><pre>[[.import name=myModule]]
[[ myModule.foo() ]]
</pre></b>
</font></td></tr></table>
<p>
A Spyce module can be any Python class that derives from
<b>spyceModule.spyceModule</b>. When it is loaded, Spyce assigns it a
__file__ attribute indicating its source location.
Do not override the <b> __init__(...)</b>
method because it is inherited from spyceModule and has an fixed signature
that is expected by the Spyce engine's module loader. The inherited method
accepts a Spyce API object, a <a
href="http://www.python.org/doc/current/lib/module-Bastion.html">Bastion</a>
of <b>spyce.spyceWrapper</b>, an internal engine object, and stores it in
<b>self._api</b>. This is the building block for all the functionality that
any module provides. The available API methods of the wrapper are (listed in
spyceModule.spyceModuleAPI):
<ul>
<li> <b>getFilename</b>: Return filename of current Spyce</li>
<li> <b>getCode</b>: Return processed Spyce (i.e. Python) code</li>
<li> <b>getCodeRefs</b>: Return python-to-Spyce code line references</li>
<li> <b>getModRefs</b>: Return list of import references in Spyce code</li>
<li> <b>getServerObject</b>: Return unique (per engine instance) server object</li>
<li> <b>getServerGlobals</b>: Return server configuration globals</li>
<li> <b>getServerID</b>: Return unique server identifier</li>
<li> <b>getModules</b>: Return references to currently loaded modules</li>
<li> <b>getModule</b>: Get module reference. The module is dynamically loaded and initialised
if it does not exist (ie. if it was not explicitly imported, but requested
by another module during processing)</li>
<li> <b>setModule</b>: Add existing module (by reference) to Spyce namespace (used for includes)</li>
<li> <b>getGlobals</b>: Return the Spyce global namespace dictionary</li>
<li> <b>registerModuleCallback</b>: Register a callback for modules change</li>
<li> <b>unregisterModuleCallback</b>: Unregister a callback for modules change</li>
<li> <b>getRequest</b>: Return internal request object</li>
<li> <b>getResponse</b>: Return internal response object</li>
<li> <b>setResponse</b>: Set internal response object</li>
<li> <b>registerResponseCallback</b>: Register a callback for when internal response changes</li>
<li> <b>unregisterResponseCallback</b>: Unregister a callback for when internal response changes</li>
<li> <b>spyceString</b>: Return a spyceCode object of a string</li>
<li> <b>spyceFile</b>: Return a spyceCode object of a file</li>
<li> <b>spyceModule</b>: Return Spyce module class</li>
<li> <b>spyceTaglib</b>: Return Spyce taglib class</li>
<li> <b>setStdout</b>: Set the stdout stream (thread-safe)</li>
<li> <b>getStdout</b>: Get the stdout stream (thread-safe)</li>
</ul> <p>
For convenience, one can sub-class the <b>spyceModulePlus</b> class instead of
the regular <b>spyceModule</b>. The spyceModulePlus defines a
<b>self.modules</b> field, which can be used to acquire references to other
modules loaded into the Spyce environment. The <i>response</i> module, for
instance, would be referenced as <i>self.modules.response</i>. Modules are
loaded on demand, if necessary. The spyceModulePlus also contains a
<b>self.globals</b> field, which is a reference to the Spyce global namespace
dictionary, though this should rarely be needed. <p>
<b>Note:</b> It is not expected that many module writers will need the entire
API functionality. In fact, the vast majority of modules will use a small
portion of the API, if at all. Many of these functions are included for just
one of the standard Spyce modules that needs to perform some esoteric
function. <p>
Three Spyce module methods, <b>start()</b>, <b>init([args])</b> and
<b>finish(error)</b> are special in that they are automatically called by the
runtime during Spyce request startup, processing and cleanup, respectively.
The modules are started in the order in which module directives appear in the
file, before processing begins. The implicitly loaded modules are always
loaded first. The init method is called during Spyce processing at the
location of the module directive in the file, with the optional args attribute
is passed as the arguments of this call. Finally, after Spyce processing is
complete, the modules are finalized in reverse order. If there is an unhandled
exception, it will be wrapped in a spyce.spyceException object and passed as
the first parameter to finish(). During successful completion of Spyce
processing (i.e. without exception), the error parameter is None. The default
inherited start, init and finish methods from spyceModule are noops. <p>
<b>Note 2:</b> When writing a Spyce module, consider carefully why you are
selecting a Spyce module over a regular Python module. If it is just code,
that does not interact with the Spyce engine, then a regular Python <font
face=courier>import</font> instead of an Spyce <font
face=courier>[[.import]]</font> can just as easily bring in the necessary
code, and is preferred. In other words, choose a Spyce module only when there
is a need for per-request initialization or for one of the engine APIs. <p>
Module writers are encouraged to look at the existing standard modules as
examples and the definitions of the core Spyce objects in <font
face=courier>spyce.py</font> as well. If you write or use a novel Spyce
module that you think is of general use, please email your
<a href="https://svn.sourceforge.net/svnroot/spyce/trunk/spyce/contrib/">contribution</a>,
or a link to it. Also, please keep in mind that the standard modules are designed
with the goal of being minimalist. Much functionality is readily available
using the Python language libraries. If you think that they should be
expanded, also please send a note. <p>
<big><a name="tag"></a><b>3.9. <font color=#ee0000>Tags</font></b></big><p>
The previous chapter discussed the Spyce module facility, the standard Spyce
modules and how users can create their own modules to extend Spyce. Spyce
functionality can also be extended via active tags, which are defined in tag
libraries. This chapter describes what Spyce active tags are, and how they are
used. We then describe each of the standard active tag libraries and, finally,
how to define <a href="#tag_new2">new tags libraries</a>. <p>
It is important, from the outset, to define what an active tag actually does.
A few illustrative examples may help. The examples below all use tags that are
defined in the <a href="#tag_core">core tag library</a>,
that has been installed under the <b>spy</b> prefix, as is the default. <p>
<ul>
<li><font face=courier><spy:parent src="parent.spi"/></font> <br>
Wraps the current page in the parent template found at parent.spi in the
same directory.
<li><font face=courier> <spy:for items="=range(5)"> <br>
<spy:print value="=foo"/> <br> </spy:for> </font>
<br> As expected, these tags will print the value of <b>foo</b>, set to
<font face=courier>bar</font> above, 5 times. </li> <p>
</ul>
<b>Common mistake:</b> Don't use [[= ]] to send values to active tag attributes:
[[= ]] sends its result directly to the output stream. And since those tokens
are parsed with higher precedence than the tag markup, Spyce won't recognize
your tag at all and will print it verbatim to the client.
Instead, prefix an expression with =, as in "=range(5)" above, and Spyce will
<i>eval</i> it before sending it to the tag.
<p>
Note that the same output could have been achieved in many different ways, and
entirely without active tags. The manner in which you choose to organize your
script or application, and when you choose active tags over other
alternatives, is a matter of personal preference. Notice also that active tags
entirely control their output and what they do with their attributes and the
result of processing their bodies (in fact, whether the body of the tag is
even processed). Tags can even supply additional syntax constraints on their
attributes that will be enforced at compile-time. Most commonly a tag could
require that certain attributes exist, and possibly that it be used only as a
single or only as a paired (open and close) tag. Unlike early versions of
HTML, active tags must be strictly balanced, and this will be enforced by the
Spyce compiler. <p>
Below, each individual standard Spyce tag library is documented, followed by a
description of how one would write a <a href="#tag_new">new
active tag library</a>. The following general information will be useful for
reading that material.
<ul>
<li>Active tags are installed using the <font
face=courier>[[.taglib]]</font>
<a href="#lang_directive">directive</a>, under some prefix.
Tag libraries may also be be loaded globally in the config module; by default
the core and form libraries are preloaded.
Active tags are of the format <font
face=courier><pre:name ... ></font>, where <b>pre</b> is the
prefix under which the tag library was installed, and <b>name</b> is defined
by the tag library. In the following tag library documentation, the prefix
is omitted from the syntax. </li>
<li>The following notation is used in the documentation of the tag libraries
below:
<ul>
<li> <name .../> : The tag should be used as a singleton. </li>
<li> <name ... > ... </name> : The tag should be used as an
open-close pair. </li>
<li> [ x (default)] : The attribute is optional. Attributes not enclosed in
brackets are required. </li>
<li> foo|<u>bar</u> : indicates that an attribute may be one of two
constant strings. The underlined value is the default. </li>
<li> <i>string</i> : an arbitrary string constant, never evaluated as Python </li>
<li> <i>exprstring</i> : may be a string constant, and may be of the form
<font face=courier>'=expr'</font>, where <font face=courier>expr</font> is
a Python expression that will be evaluated in the tag context. </li>
<li> <i>expr</i>: a Python expression. (Currently only the "data" parameters
of some form and core tags use this rather than exprstring.)
</li>
<li> <i>exports foo, *bar</i> Exports to the parent context
the variable foo and the variable
with the name given by the expression bar. Normally, implementation
details of tags will not affect the parent context, so you do not have
to worry about your variables being clobbered. Tags may, however,
export specific parts of their own context to the parent.
See, for example,
the let and for tags in the core taglib.
<b>Note:</b> exporting of variables whose name
cannot be determined at compile time is deprecated, and will be removed in Spyce 2.2.
</li>
</ul>
</ul> <p>
<big><a name="tag_core"></a><b>3.9.1. <font color=#ee0000><i>Core</i></font></b></big><p>
The core tag library is aliased as "spy" by default.
<p>
This library contains various frequently-used tags: the parent tag, login tags,
and some tags for generating lists and tables from Python iterators.
<p>
<big><b>Parent Tag</b></big>
<ul>
<li><font face=courier><<b><a name=parent>parent</a></b> [src=<i>url</i>] [other parameters] /></font>
<br>
Specifies a parent template to apply to the current page, which
is passed to the parent as child._body. Any extra parameters are also
passed in the child dictionary. If src is not given, 'parent.spi' used if it exists
in the current directory; otherwise, the default parent
is used as specified in the config module.
<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/hello-templated.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><font color="#229922"><spy:parent title="Hello, world!" /></font>
<font color="#ff7448">[[-- Spyce can embed chunks of Python code, like this. --]]</font>
<font color="#0000CC">[[\
import time
t = time.ctime()
]]</font>
<font color="#ff7448">[[--
pull the count from the GET variable.
the int cast is necessary since request stores everything as a string
--]]</font>
<font color="#0000CC">[[ for i in range(int(request.get1('count', 2))):{ ]]</font>
<div>Hello, world!</div>
<font color="#0000CC">[[ } ]]</font>
The time is now <font color="#CC0000">[[= t ]]</font>.
</b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/hello-templated.spy">Run this code</a></b>
</font>
</td></tr></table>
</ul>
<big><b>Login Tags</b></big>
<ul>
<li><font face=courier><<b><a name=login>login</a></b> [validator=<i>function name</i>] /></font>
<br>
Generates a login form according to the template specified in your config file.
If <b>validator</b> is not specified, the default validator from your config file is used.
(<b>validator</b> may be the name of a function in a different Python module;
just prefix it with the module name and Spyce will automaticall import it when
necessary.)
<p>
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/login-optional.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><html><body>
<font color="#229922"><f:form></font>
<font color="#0000CC">[[ if request.login_id():{ ]]</font>
You are logged in with user id <font color="#CC0000">[[= request.login_id() ]]</font>
<font color="#229922"><spy:logout /></font>
<font color="#0000CC">[[ } else: { ]]</font>
<font color="#229922"><spy:login /></font>
You are not logged in. (You may login with username/password: spyce/spyce.)
<font color="#0000CC">[[ } ]]</font>
<p>
(Here is some content that is the same before and after login.)
<font color="#229922"></f:form></font>
</body></html></b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/login-optional.spy">Run this code</a></b>
</font>
</td></tr></table>
<li><font face=courier><<b><a name=logout>logout</a></b> /></font>
<br>
Generates a logout button that will clear the cookie generated by <b>login</b>
and <b>login_required</b>.
<p>
<li><font face=courier><<b><a name=login_required>login_required</a></b> [validator=<i>function name</i>] /></font>
<br>
If a valid login cookie is not present, generates a login form according to the
template specified in your config file, then halts execution of the current page.
<p>
(You may log in to this example as user <b>spyce</b>, password <b>spyce</b>.)
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/login-required.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><font color="#229922"><spy:parent title="Login example" /></font>
<font color="#229922"><f:form></font>
<font color="#229922"><spy:login_required /></font>
You are logged in.
<font color="#229922"><spy:logout /></font>
<font color="#229922"></f:form></font>
</b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/login-required.spy">Run this code</a></b>
</font>
</td></tr></table>
</ul>
<big><b>Convenience Tags</b></big>
<p>
These tags are shortcuts for creatings lists and tables. As with
the <a href="#tag_form">form tag library</a>, any python
iterator may be given as the data parameter. Also as with the form tags,
any unrecognized parameters will be passed through to the generated HTML.
<ul>
<li><font face=courier><<b><a name=ul>ul</a></b> data=<i>expr</i>] /></font>
<br>
Convenience tag for the common use of ul; equivalent to
<blockquote>
<tt><pre>
<ul>
[[ for item in data:{ ]]
<li>[[= item ]]</li>
[[ } ]]
</ul>
</pre></tt>
</blockquote>
<li><font face=courier><<b><a name=ol>ol</a></b> data=<i>expr</i>] /></font>
<br>
Like ul, but for ordered lists.<br><br>
<li><font face=courier><<b><a name=dl>dl</a></b> data=<i>expr</i>] /></font>
<br>
Convenience tag for the common use of dl; equivalent to
<blockquote>
<tt><pre>
<dl>
[[ for term, desc in data:{ ]]
<dt>[[= term ]]</dt>
<dd>[[= desc ]]</dd>
[[ } ]]
</dl>
</pre></tt>
</blockquote>
<li><font face=courier><<b><a name=table>table</a></b> data=<i>expr</i>] /></font>
<br>
Convenience tag for the common use of table; equivalent to
<blockquote>
<tt><pre>
<table>
[[ for row in data:{ ]]
<tr>
[[ for cell in row:{ ]]
<td>[[= cell ]]</td>
[[ } ]]
</tr>
[[ } ]]
</table>
</pre></tt>
</blockquote>
</ul>
<big><a name="tag_form"></a><b>3.9.2. <font color=#ee0000><i>Form</i></font></b></big><p>
The form tag library is aliased as "spy" by default.
<p>
This library simplifies the generation and handling of forms by
automating away repetitive tasks. Let's take a look at a simple example:
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/formintro.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><font color="#229922"><spy:parent title="Form tag intro" /></font>
<font color="#229922"><f:form></font>
<div class="simpleform">
<font color="#229922"><f:text name="text1" label="Text input" default="change me" size=10 maxlength=30 /></font>
<font color="#229922"><f:text name="text2" label="Text input 2" value="change me" size=10 maxlength=30 /></font>
</div>
<fieldset style="clear: both">
<legend>One or two? Or both?</legend>
<font color="#229922"><f:checkboxlist class="radio" name="checkboxlist" data="[(1, 'one'), (2, 'two')]" /></font>
</fieldset>
<div style="clear: both"><font color="#229922"><f:submit /></font></div>
<font color="#229922"></f:form></font></b></font></pre>
</font>
</td></tr><tr><td align=right bgcolor="#cccccc">
<font face="arial, helvetica" size="-1">
<b><a href="/docs/examples/formintro.spy">Run this code</a></b>
</font>
</td></tr></table>
<p>This demonstrates several properties of Spyce form tags:
<ul>
<li>Most tags take an optional label parameter; this is turned into an HTML label tag
associated with the form element itself.
<li>If you View Source in your browser while running this sample, you can
see that Spyce generates an id with the same value as the name parameter.
(You can override this by explicitly specifying a different id parameter,
if you need.)
<li>You can pass arbitrary parameters (such as the class parameter for <f:form>)
to a Spyce form tag; parameters that do not have special meaning to Spyce
will be passed through to the HTML output.
<li>Try changing the form values and submitting. By default, Spyce automatically
remembers the user input for you, unless you give a tag a <i>value</i>
parameter (or <i>selected</i> for collection elements), which has highest precedence.
Note the different behavior of text1 and text2 in this example.
<li>Spyce provides some higher-level tags such as checkboxlist that result in multiple
elements at the HTML level. For these tags, a "data" parameter is expected,
which is always interpreted as a Python expression.
Any iterable may be used as data, including generators and generator expressions
for those with recent Python versions. Typically data would come from the
database; here we're just using a literal list.
</ul>
<big><b>Handlers</b></big>
<p>
Active Handlers allow you to "attach" python functions to form submissions.
They are described in the
<a href="#tag_handlers">Active Handlers</a> manual page.
<p>
<big><b>Reference</b></big>
<p>
First, some general rules:
<p>The text displayed by a text-centric tag can come from one of three
places. In order of decreasing priority, these are
<ul>
<li>the <b>value</b> parameter
<li>the value submitted by the user is used
<li>the <b>default</b> parameter
</ul>
If none of these are found, the input will be empty.
<p>For determining whether option, radio, and checkbox tags are checked or selected,
a similar process is followed, with
<b>selected</b> and <b>checked</b> parameters as the highest-priority source.
The same parameters are used for select, radiolist, and checkboxlist tags;
the only difference is for the collection tags, you can also specify
multiple values in a Python list (or other iterable) in either the
<b>selected/checked</b> or <b>default</b> parameters.
<p>All tags except form and submit can be given names that tell Spyce how to
treat their submitted values when passing to an Active Handler function.
Adding ":int", ":float", ":bool", or ":list" is allowed. The first three
tell Spyce what kind of Python value to convert the submission to; ":list"
may be combined with these, or used separately, and tells Spyce to pass a
list of all values submitted instead of a single one.
(An example is given in the <a href="#tag_handlers">Active Handlers</a> page.)
Finally, here is the list of tags:
<ul>
<li><font face=courier><<b>form</b>
[method=<i>exprstring</i>] [action=<i>exprstring</i>] ...> </<b>form</b>></font>
<br> Begin a new form. The <b>method</b> parameter defaults
to 'POST'. The <b>action</b> parameter defaults to the current page.
</li> <p>
<li>
<font face=courier><<b><a name=submit>submit</a></b>
[handler=<i>exprstring</i>] [value=<i>exprstring</i>] ... />
</font>
<br>Create a submit button. The <b>value</b> parameter is
emitted as the button text. If <b>handler</b> is given, Spyce will call the function(s)
it represents at the beginning of the page load after this button is clicked.
(Multiple function names may be separated with commas.)
<p>
If the handler is in a different [python] module, Spyce will automatically import it
before use.
<p>
A handler may take zero or more arguments.
For the first non-self argument (if present), Spyce always passes a
moduleFinder corresponding to the current
spyceWrapper object; it is customary to call this argument "api."
moduleFinder provides __getitem__ access to loaded modules; thus,
"api.request" would be the current request module. If a requested module
is not found, it is loaded.
<p>
(You can also directly access the wrapper with api._wrapper, providing
access to <a href="#mod_new">anything module authors have</a>,
but you will rarely if ever need to do this.)
<p>
For other handler function parameters, Spyce will pass the values for the
corresponding form input, or None if nothing was found in the GET or POST
variables.
<p>
See also the <a href="#tag_handlers">Active Handlers</a>
language section for a higher-level overview.
<p>
Limitation: currently, Active Handlers require resubmitting to the same spyce page;
of course, the handler method may perform an internal or external
<a href="#mod_redirect">redirect</a>.
</li> <p>
<li><font face=courier><<b>hidden</b>
name=<i>exprstring</i> [value=<i>exprstring</i>] [default=<i>exprstring</i>] .../></font>
<br> Create a hidden form field. The <b>name</b> parameter is evaluated and
emitted.
</li> <p>
<li><font face=courier><<b>text</b>
name=<i>exprstring</i> [value=<i>exprstring</i>] [default=<i>exprstring</i>] .../></font>
<br> Create a form text field. The <b>name</b> parameter is evaluated and
emitted.
</li> <p>
<li><font face=courier><<b>date</b>
name=<i>exprstring</i> [value=<i>exprstring</i>] [default=<i>exprstring</i>] [size=<i>exprstring</i>] [format=<i>exprstring</i>] .../></font>
<br> Create a form text field with a javascript date picker. Format defaults to MM/DD/YYYY. Maxlength is always len(format);
this is also the default size, but size may be overridden for aesthetics.
<li><font face=courier><<b>password</b>
name=<i>exprstring</i> [value=<i>exprstring</i>] [default=<i>exprstring</i>] [size=<i>exprstring</i>] [maxlength=<i>exprstring</i>] .../></font>
<br> Create a form password field. Parameters are the same as for <b>text</b>
fields, explained above. </li> <p>
<li><font face=courier><<b>textarea</b>
name=<i>exprstring</i> [value=<i>exprstring</i>] [rows=<i>exprstring</i>] [cols=<i>exprstring</i>] ...>default</<b>textarea</b>></font>
<br> Create a form textarea field. The <b>name</b> parameter is evaluated and
emitted. The <b>value</b> optional parameter is evaluated. A <b>default</b>
may be provided in the body of the tag. The value emitted is, in order of
decreasing priority: local tag value, value in submitted
request dictionary, local tag default. We search this list
for the first non-None value. The <b>rows</b> and <b>cols</b> optional
parameters are evaluated and emitted. </li> <p>
<li><font face=courier><<b>radio</b>
name=<i>exprstring</i> value=<i>exprstring</i> [checked] [default] .../></font>
<br> Create a form radio-box. The <b>name</b> and <b>value</b> parameters are
evaluated and emitted. A <b>checked</b> and <b>default</b> flags affect
whether this box is checked. The box is checked based on the following
values, in decreasing order of priority: tag value,
value in submitted request dictionary, tag default.
We search this list for the first non-None value. </li> <p>
<li><font face=courier><<b>checkbox</b>
name=<i>exprstring</i> value=<i>exprstring</i> [checked] [default] .../></font>
<br> Create a form check-box. Parameters are the same as for <b>radio</b>
fields, explained above. </li> <p>
<li><font face=courier><<b>select</b>
name=<i>exprstring</i> [selected=<i>exprstring</i>] [default=<i>exprstring</i>] [data=<i>expr</i>] ...>...</<b>select</b>></font>
<br> Create a form select block. The <b>name</b> parameter is
evaluated and emitted. The optional <b>data</b> should be an iterable of
(description, value) pairs.
</li><p>
<li><font face=courier><<b>option</b>
[text=<i>exprstring</i>] [value=<i>exprstring</i>] [selected] [default] .../></font>
<br> <font face=courier><<b>option</b>
[value=<i>exprstring</i>] [selected] [default] ...>text</<b>option</b>></font>
<br> Create a form selection option. This tag must be nested within a
<b>select</b> tag. The <b>text</b> optional parameter is evaluated and
emitted in the body of the tag. It can also be provided in the body of the
tag, as you might be used to seeing in HTML.
</li> <p>
<li><font face=courier><<b>radiolist</b>
name=<i>exprstring</i> data=<i>expr</i> [checked=<i>exprstring</i>] [default=<i>exprstring</i>] ...>...</<b>select</b>></font>
<br> Create multiple radio buttons from <b>data</b>, which should be an iterable of
(description, value) pairs.
</li><p>
<li><font face=courier><<b>checkboxlist</b>
name=<i>exprstring</i> data=<i>expr</i> [checked=<i>exprstring</i>] [default=<i>exprstring</i>] ...>...</<b>select</b>></font>
<br> Create multiple checkboxes from <b>data</b>, which should be an iterable of
(description, value) pairs.
</li><p>
</ul> <p>
Here is an example of all of these tags in use:
<table border=1 align=center>
<tr><td align=left bgcolor="#cccccc">
<font face="arial, helvetica" size="-1"><b>examples/formtag.spy</b></font>
</td></tr><tr><td>
<font face=courier>
<pre style='font-family: courier,monospace; font-size: small'><font color="#000000"><b><font color="#229922"><spy:parent title="Form tag example" /></font>
<font color="#229922"><f:form></font>
<h2>Primitive controls</h2>
<div class="simpleform">
<font color="#229922"><f:text name="mytext" label="Text" default="some text" size=10 maxlength=30 /></font>
<font color="#229922"><f:password name="mypass" label="Password" default="secret" /></font>
<font color="#229922"><f:textarea name="mytextarea" label="Textarea" default="rimon" rows=2 cols=50></font><font color="#229922"></f:textarea></font>
<label for="mycheck">Checkbox</label><font color="#229922"><f:checkbox name=mycheck value=check1 /></font>
<label for="myradio1">Radio option 1</label><font color="#229922"><f:radio name=myradio value=option1 /></font>
<label for="myradio2">Radio option 2</label><font color="#229922"><f:radio name=myradio value=option2 /></font>
</div>
<div style="clear: both">
<h2 style="padding-top: 1em;">Compound controls</h2>
<font color="#ff7448">[[-- a simple data source for the compound controls -- in practice