XHP for Python
Project description
XHPy extends Python syntax such that XML document fragments become valid Python expressions. It is based off XHP, a similar framework for PHP.
Advantages
Simplicity: write UI logic in a simple, expressive syntax without the need for external templates or templating languages.
Flexibility: use Python expressions freely within XHPy tags, and vice-versa.
Security: benefit from automatic escaping of text within XHPy tags.
Reusability: build reusable components by subclassing :x:element.
An example
In bar.py:
from xhpy.init import register_xhpy_module register_xhpy_module('foo') import foo
In foo.py:
from xhpy.pylib import * class :ui:foo(:x:element): attribute list bar category %flow def render(self): a = <ul /> for b in self.getAttribute('bar'): a.appendChild(<li>{b}</li>) return a print <div class="baz"><ui:foo bar={range(3)} /></div>
We can now run bar.py as a normal Python script:
$ python bar.py <div class="baz"><ul><li>0</li><li>1</li><li>2</li></ul></div>
Congratulations! You just wrote your first snippet of XHPy.
Syntax
XHPy adds some new syntax to Python. Line by line replay time!
from xhpy.init import register_xhpy_module
This initializes XHPy and allows you to register modules to be interpreted as XHPy.
register_xhpy_module('foo')
Now the foo module in foo.py will be interpreted as XHPy when imported. If foo were a package, all of its submodules would also be registered; this is useful for registering UI libraries.
import foo
To actually use XHPy, however, you will probably want the core library:
from xhpy.pylib import *
Now you have access to all the standard HTML 4.0 elements, the :x:element base class (this is what you build custom components on top of!), and some utilities.
class :ui:foo(:x:element):
Making new components is easy: just subclass :x:element. For your component class to be registered, it must start with : - this clearly distinguishes your components from ordinary Python classes.
attribute list bar
This is an attribute declaration, meaning that :ui:foo allows bar attributes on <ui:foo> tags. Note the
<ui:foo bar={range(3)} />
later on - like XHP, XHPy uses XML attribute syntax.
category %flow
This is a category declaration - :ui:foo is part of the %flow category. Categories are primarily useful as a way of identifying elements that are similar without using inheritance; for example, the <a> tag in pylib.html has
children (pcdata | %flow)*
indicating that its children must either contain text or be of the %flow category. (So we can put <ui:foo> inside <a>!)
def render(self):
When you print an :x:element (or call str on it), the render() method is invoked; this is where you put your UI logic.
a = <ul /> for b in self.getAttribute('bar'): a.appendChild(<li>{b}</li>) return a
Here, <ui:foo> is a thin wrapper around <ul> that allows you to construct an unordered list out of a Python list. Standard HTML elements like <ul> and <li> are automatically rendered - except that, in XHPy, you can use Python expressions within tags, so that
{b}
is replaced by the value of b. Note the use of getAttribute() and appendChild():
self.getAttribute('bar')
fetches the value of attribute bar (in this case, range(3)), whereas
a.appendChild(<li>{b}</li>)
adds <li>{b}</li> as a child of a = <ul />.
XHPy is largely based off XHP; for more details on the latter, see the XHP wiki. The syntax has been adapted for Python; in particular:
there are no semicolons;
XHPy class names may be used anywhere ordinary Python classes can;
XHPy tags ignore internal whitespace, but must externally obey indentation and line continuation rules.
More on the last point:
def foo(href): return <a href={href}></a> def bar(href): return\ <a href={href}></a>
are valid, whereas
def foo(href): return\ <a href={href}> </a>
is not, as it introduces an extra dedent after </a>.
How it works
When you
import xhpy.init
XHPy installs an import hook. This hook traps subsequent import statements, running them through a preprocessor that parses a superset of Python. This preprocessor translates XHPy tags and class names to valid Python, then executes the translated code in module scope.
This is similar to how XHP works, except:
with, e.g., pythonenv, you can always use XHPy even without access to system-wide Python package installation directories;
by default, Python compiles bytecode .pyc files from your modules, so the preprocessing only needs to be done once when a module is first imported.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.