169 lines
5.7 KiB
Plaintext
169 lines
5.7 KiB
Plaintext
====================================================
|
|
py.xml: Lightweight and flexible xml/html generation
|
|
====================================================
|
|
|
|
|
|
Motivation
|
|
==========
|
|
|
|
There are a plethora of frameworks and libraries to generate
|
|
xml and html trees. However, many of them are large, have a
|
|
steep learning curve and are often hard to debug. Not to
|
|
speak of the fact that they are frameworks to begin with.
|
|
|
|
The py lib strives to offer enough functionality to represent
|
|
itself and especially its API in html or xml.
|
|
|
|
.. _xist: http://www.livinglogic.de/Python/xist/index.html
|
|
|
|
a pythonic object model , please
|
|
================================
|
|
|
|
The py lib offers a pythonic way to generate xml/html, based on
|
|
ideas from xist_ which `uses python class objects`_ to build
|
|
xml trees. However, xist_'s implementation is somewhat heavy
|
|
because it has additional goals like transformations and
|
|
supporting many namespaces. But its basic idea is very easy.
|
|
|
|
.. _`uses python class objects`: http://www.livinglogic.de/Python/xist/Howto.html
|
|
|
|
generating arbitrary xml structures
|
|
-----------------------------------
|
|
|
|
With ``py.xml.Namespace`` you have the basis
|
|
to generate custom xml-fragments on the fly::
|
|
|
|
class ns(py.xml.Namespace):
|
|
"my custom xml namespace"
|
|
doc = ns.books(
|
|
ns.book(
|
|
ns.author("May Day"),
|
|
ns.title("python for java programmers"),),
|
|
ns.book(
|
|
ns.author("why"),
|
|
ns.title("Java for Python programmers"),),
|
|
publisher="N.N",
|
|
)
|
|
print doc.unicode(indent=2).encode('utf8')
|
|
|
|
will give you this representation::
|
|
|
|
<books publisher="N.N">
|
|
<book>
|
|
<author>May Day</author>
|
|
<title>python for java programmers</title></book>
|
|
<book>
|
|
<author>why</author>
|
|
<title>Java for Python programmers</title></book></books>
|
|
|
|
In a sentence: positional arguments are child-tags and
|
|
keyword-arguments are attributes.
|
|
|
|
On a side note, you'll see that the unicode-serializer
|
|
supports a nice indentation style which keeps your generated
|
|
html readable, basically through emulating python's white
|
|
space significance by putting closing-tags rightmost and
|
|
almost invisible at first glance :-)
|
|
|
|
basic example for generating html
|
|
---------------------------------
|
|
|
|
Consider this example::
|
|
|
|
from py.xml import html # html namespace
|
|
|
|
paras = "First Para", "Second para"
|
|
|
|
doc = html.html(
|
|
html.head(
|
|
html.meta(name="Content-Type", value="text/html; charset=latin1")),
|
|
html.body(
|
|
[html.p(p) for p in paras]))
|
|
|
|
print unicode(doc).encode('latin1')
|
|
|
|
Again, tags are objects which contain tags and have attributes.
|
|
More exactly, Tags inherit from the list type and thus can be
|
|
manipulated as list objects. They additionally support a default
|
|
way to represent themselves as a serialized unicode object.
|
|
|
|
If you happen to look at the py.xml implementation you'll
|
|
note that the tag/namespace implementation consumes some 50 lines
|
|
with another 50 lines for the unicode serialization code.
|
|
|
|
CSS-styling your html Tags
|
|
--------------------------
|
|
|
|
One aspect where many of the huge python xml/html generation
|
|
frameworks utterly fail is a clean and convenient integration
|
|
of CSS styling. Often, developers are left alone with keeping
|
|
CSS style definitions in sync with some style files
|
|
represented as strings (often in a separate .css file). Not
|
|
only is this hard to debug but the missing abstractions make
|
|
it hard to modify the styling of your tags or to choose custom
|
|
style representations (inline, html.head or external). Add the
|
|
Browers usual tolerance of messyness and errors in Style
|
|
references and welcome to hell, known as the domain of
|
|
developing web applications :-)
|
|
|
|
By contrast, consider this CSS styling example::
|
|
|
|
class my(html):
|
|
"my initial custom style"
|
|
class body(html.body):
|
|
style = html.Style(font_size = "120%")
|
|
|
|
class h2(html.h2):
|
|
style = html.Style(background = "grey")
|
|
|
|
class p(html.p):
|
|
style = html.Style(font_weight="bold")
|
|
|
|
doc = my.html(
|
|
my.head(),
|
|
my.body(
|
|
my.h2("hello world"),
|
|
my.p("bold as bold can")
|
|
)
|
|
)
|
|
|
|
print doc.unicode(indent=2)
|
|
|
|
This will give you a small'n mean self contained
|
|
represenation by default::
|
|
|
|
<html>
|
|
<head/>
|
|
<body style="font-size: 120%">
|
|
<h2 style="background: grey">hello world</h2>
|
|
<p style="font-weight: bold">bold as bold can</p></body></html>
|
|
|
|
Most importantly, note that the inline-styling is just an
|
|
implementation detail of the unicode serialization code.
|
|
You can easily modify the serialization to put your styling into the
|
|
``html.head`` or in a separate file and autogenerate CSS-class
|
|
names or ids.
|
|
|
|
Hey, you could even write tests that you are using correct
|
|
styles suitable for specific browser requirements. Did i mention
|
|
that the ability to easily write tests for your generated
|
|
html and its serialization could help to develop _stable_ user
|
|
interfaces?
|
|
|
|
More to come ...
|
|
----------------
|
|
|
|
For now, i don't think we should strive to offer much more
|
|
than the above. However, it is probably not hard to offer
|
|
*partial serialization* to allow generating maybe hundreds of
|
|
complex html documents per second. Basically we would allow
|
|
putting callables both as Tag content and as values of
|
|
attributes. A slightly more advanced Serialization would then
|
|
produce a list of unicode objects intermingled with callables.
|
|
At HTTP-Request time the callables would get called to
|
|
complete the probably request-specific serialization of
|
|
your Tags. Hum, it's probably harder to explain this than to
|
|
actually code it :-)
|
|
|
|
.. _`py.test`: test/index.html
|