Hi everyone,
This is a request-for-speculation. I've been thinking about an extension
to PTL that I'd like to make, and wondered if you might scan my example
below & provide feedback.
What I'd like to do is add a block-level syntax to PTL. The ability to
define blocks of HTML/XML as explicit blocks in Python code would
increase readability and maintainability.
Here's an example of a possible extended-PTL syntax:
from extension_tags import * # some tag definitions
import my_database
people = my_database.get_people()
title = 'List of all people'
# a custom block definition, using a str() expression
alert = 'div style="color: red;"'
HTML: # HTML is a tag instance
HEAD:
TITLE:
title
LINK(rel='stylesheet', type='text/css',
href='/main.css')
BODY:
H1:
title
P:
'This is a list of the '
len(people)
'people defined in the sample application.'
alert:
"Kids, don't try this at home!"
TABLE(_class='nice_table'): # or some other class-hack
TR:
TH('surname')
TH('first name')
TH('email')
TH('--actions--')
for person in people: # still have Python blocks!
TR:
TD(person.surname)
TD(person.firstname)
TD(person.email)
TD:
A(href='%d/edit' % person.id):
'Edit'
A('Delete', # *args-style tag syntax
href='%d/delete' % person.id)
Notes:
* whenever a block-level statement is encountered, and the
statement does not begin with a Python keyword (e.g. 'def', 'if',
'while', ...), then the modified parser will treat the statement
as an "extended PTL statement".
* The upper-casing in my example is purely optional, I just think it
lends to readability.
* the expression in an ePTL opening-line should resolve to either an
object that has methods 'open_block' and 'close_block', or it must
be a string; if not, str() will be used to stringify it.
* If the expression resolves to an object, then its open_block()
method is called before the nested lines are evaluated. After the
nested lines are evaluated, then the close_block() method is
called. Presumably these would write opening and closing tags.
* Otherwise, the str() of the epxression is used as the opening-tag
content, and the first word of the str() is used as the closing
tag. Thus the expression 'BODY id="foo":' would lead to an opening
tag and a closing tag .
* Inlining: Optionally, the str() of a tag object itself should
render as a single tag. In the example above, "A" is used both as
a block expression but also as an inline expression. (This has
nothing to do with the block-parsing proposal really, it's just is
a nice application of it.)
* I've considered a tag-inlining syntax, so that you could write
one-liners like "UL: LI: A('Main', href='/')". I'm torn, though;
while more efficient, it would be harder to parse, and contravenes
the Python-style recommendation against inlining block-level
expressions.
* This type of syntax would work well with HTML and XML. XML
namespaces, CDATA and comment blocks, etc. should not pose any
problems, and should be codeable with the same regular syntax.
Theoretically it could do plain-text as well, but that's not the
target application.
Code that uses this syntax is, of course, not Python. However, tools
that work well with Python syntax (such as Emacs python-mode) will
probably recognize the block-level syntax, leading to easy composition
and editing. While it isn't Python, it's much more like Python than,
e.g. TAL or Cheetah is.
I think it takes the regularity of 'stan' syntax, and adds great
readability through the block syntax, without requiring map() or
listcomps to get iteration, or other declarative hacks to handle
conditionals in the midst of a complex page.
I'm probably going to write this up, and give it a try in a few little
applications. But if you could spare a minute to critique the idea, I'd
really appreciate your comments.
Thanks,
Graham