From paul at codespeak.net Wed Jul 5 18:09:58 2006 From: paul at codespeak.net (paul at codespeak.net) Date: Wed, 5 Jul 2006 18:09:58 +0200 (CEST) Subject: [z3-checkins] r29645 - in z3/deliverance/branches: . namespaced Message-ID: <20060705160958.CB71F1005A@code0.codespeak.net> Author: paul Date: Wed Jul 5 18:09:57 2006 New Revision: 29645 Added: z3/deliverance/branches/ z3/deliverance/branches/namespaced/ Log: Moving the namespaced stuff over From paul at codespeak.net Wed Jul 5 20:50:30 2006 From: paul at codespeak.net (paul at codespeak.net) Date: Wed, 5 Jul 2006 20:50:30 +0200 (CEST) Subject: [z3-checkins] r29651 - in z3/deliverance/branches/namespaced: . content doc etc themes themes/simple Message-ID: <20060705185030.9507C100AC@code0.codespeak.net> Author: paul Date: Wed Jul 5 20:50:26 2006 New Revision: 29651 Added: z3/deliverance/branches/namespaced/README.txt z3/deliverance/branches/namespaced/content/ z3/deliverance/branches/namespaced/content/localhello.html z3/deliverance/branches/namespaced/deliverance.py z3/deliverance/branches/namespaced/doc/ z3/deliverance/branches/namespaced/doc/NOTES.rst z3/deliverance/branches/namespaced/etc/ z3/deliverance/branches/namespaced/etc/appmap.xml z3/deliverance/branches/namespaced/etc/themecontent.xml z3/deliverance/branches/namespaced/etc/themerules.xml z3/deliverance/branches/namespaced/modpython.conf z3/deliverance/branches/namespaced/modpython.py z3/deliverance/branches/namespaced/renderer.xsl z3/deliverance/branches/namespaced/themecompiler.xsl z3/deliverance/branches/namespaced/themes/ z3/deliverance/branches/namespaced/themes/simple/ z3/deliverance/branches/namespaced/themes/simple/sampletheme.xml z3/deliverance/branches/namespaced/themes/simple/simpletheme.css Log: First commit of refactored Deliverance, sans the content publishing part Added: z3/deliverance/branches/namespaced/README.txt ============================================================================== --- (empty file) +++ z3/deliverance/branches/namespaced/README.txt Wed Jul 5 20:50:26 2006 @@ -0,0 +1,15 @@ +======================================= +Deliverancy, high-speed themes for Zope +======================================= + +Quick Start +----------- + +1) Install lxml. + +2) cd to the directory containing this README. + +3) python ./deliverance.py + +This runs the timeit function, showing average time to apply a simple theme. + Added: z3/deliverance/branches/namespaced/content/localhello.html ============================================================================== --- (empty file) +++ z3/deliverance/branches/namespaced/content/localhello.html Wed Jul 5 20:50:26 2006 @@ -0,0 +1,12 @@ + + + +
+Hello world
+ + \ No newline at end of file Added: z3/deliverance/branches/namespaced/deliverance.py ============================================================================== --- (empty file) +++ z3/deliverance/branches/namespaced/deliverance.py Wed Jul 5 20:50:26 2006 @@ -0,0 +1,156 @@ + +import os +from lxml import etree +from time import time +from lxml.etree import Namespace, ElementBase + + +nsmap = { + "dv": "http://www.plone.org/deliverance", + "html": "http://www.w3.org/1999/xhtml", + "xsl": "http://www.w3.org/1999/XSL/Transform", + "at": "http://plone.org/archetypes", + } + +class AppMap: + + def __init__(self): + + # Open the appmap file, make a tree, and process XIncludes + self.module_dir = os.path.dirname(os.path.abspath(__file__)) + layoutsfn = os.path.join(self.module_dir, "etc/appmap.xml") + self.tree = etree.ElementTree(file=layoutsfn) + self.tree.xinclude() + + # Make a themeprocessor to style all outgoing pages. Note that the + # .processor attribute comes from an lxml namespace binding, meaning it is + # defined via a custom Python class defined below (class LayoutElement) + root = self.tree.getroot() + layout = root.xpath("dv:layouts/dv:layout", nsmap)[0] + self.themeprocessor = layout.processor + + + def publish(self, xmlstring): + """Given a string of XML, theme it""" + + # Stage 1 and 2, get an etree for the rendered resource + resource = etree.XML(xmlstring) + + # Stage 3, apply theme + response = str(self.themeprocessor(resource)) + + return response + +# The following are extensions based on lxml namespace extensions. It +# adds Python behavior to XML nodes. + +class DVRuleBase(ElementBase): + + def getThemeNode(self): + """Get a node in the theme doc""" + + # Current node is a rule, get xpath from the @theme attr + themedoc = self.xpath("../../dv:theme", nsmap)[0][0] + xpath = self.get("theme") + try: + themenode = themedoc.xpath(xpath, nsmap)[0] + except IndexError: + msg = "Themedoc has no node at: %s" % xpath + print msg + themenode = None + + return themenode + + +class LayoutElement(ElementBase): + + def processor(self): + """Make XSLT processor by changing theme based on rules""" + + # Apply all the rules + for rule in self.xpath("./dv:rules/*", nsmap): + rule.apply() + + # Merge applied rules into compilerdoc + compilerroot = self.xpath("../dv:compiler/xsl:stylesheet", nsmap)[0] + themeroot = self.xpath("dv:theme/html:html", nsmap)[0] + target = compilerroot.xpath("xsl:template[@match='/']", nsmap)[0] + target.append(themeroot) + + #print etree.tostring(compilerroot) + + return etree.XSLT(compilerroot) + + processor = property(processor) + + +class RuleReplaceElement(DVRuleBase): + + def apply(self): + # TODO: Someething here + themenode = self.getThemeNode() + if themenode is None: + return + del(themenode[:]) + themenode.text = None + xslvalueof = etree.SubElement(themenode, + "{%s}value-of" % nsmap["xsl"]) + xslvalueof.set("select", self.get("content")) + + +class RuleCopyElement(DVRuleBase): + + def apply(self): + themenode = self.getThemeNode() + if themenode is None: + return + del(themenode[:]) + themenode.text = None + xslvalueof = etree.SubElement(themenode, + "{%s}apply-templates" % nsmap["xsl"]) + xslvalueof.set("select", self.get("content")) + + +class RuleAppendElement(DVRuleBase): + + def apply(self): + themenode = self.getThemeNode() + if themenode is None: + return + xslvalueof = etree.SubElement(themenode, + "{%s}apply-templates" % nsmap["xsl"]) + xslvalueof.set("select", self.get("content")) + + +# lxml Namespace support +namespace = Namespace(nsmap['dv']) +namespace['layout'] = LayoutElement +namespace['replace'] = RuleReplaceElement +namespace['copy'] = RuleCopyElement +namespace['append'] = RuleAppendElement + + +def testit(xmlstring): + + appmap = AppMap() + result = appmap.publish(xmlstring) + + return result + +def timeit(xmlstring): + appmap = AppMap() + start = time() + iters = 50 + for i in range(iters): + result = appmap.publish(xmlstring) + print result[0:2000] + print "Average time:", (time() - start) / iters + +def main(): + xmlstring = open("content/localhello.html").read() + timeit(xmlstring) + #testit(path1) + +if __name__ == "__main__": + result = main() + print result Added: z3/deliverance/branches/namespaced/doc/NOTES.rst ============================================================================== --- (empty file) +++ z3/deliverance/branches/namespaced/doc/NOTES.rst Wed Jul 5 20:50:26 2006 @@ -0,0 +1,43 @@ +=============== +Random Notes +=============== + +This file collects random points to weave into other document docs. + +0) You can do runtime creation of themes from remote URLs. This is a lot easier than +you'd think. It could be possible to even build a reasonably smart, productive web +front end for finding the plug points on each side. (About the only part that would +take some thinking is URL rewriting for stuff that keeps getting served by another +host, such as images and CSS.) + +1) The mod_python integration is done as a handler rather than a filter. This is just +historical: In something else, am currently using the module to also resolve certain +URLs that are managed in an XML "map". + +2) For the XML "map" stuff, xml:id support is what makes it so fast. However, this +imposes some limitations. For example, you can't have slashes in xml:id values. + +3) Yeh, it doesn't have tests, other than the timeit function at the bottom +of deliverance.py. I'm not yet much of a programmer. I hope to fix this deficiency +during downtime in July. + +4) Neat point: Because of XInclude, the appmap XML document has everything it +needs, including the generated stuff, in a view-source friendly format. Want to +see what's happening? Just dump the XML document and look at it. + +5) The theme doesn't have to be well-formed XML. The HTMLParser can handle garbage as +input and generate well-formed (though perhaps not valid) stuff on output. + +6) The append rule in etc/themerules.xml shows that you can easily copy page-specific +CSS, JS, etc. from the content document's into the resulting . + +7) Extensibility is provided through XML namespaces and lxml's namespace binding +support. Want a new rule? Just add it and bind a Python handler to it. + +8) The "compilation" step provides a nice opportunity to accomplish two goals: + +a. Make things simple. Deliverance doesn't expose XSLT to users. Other things +can be hidden as well. + +b. Optimize. If there are calculations that are dynamic, but only calculated +once, they can be moved into this little pipeline. Added: z3/deliverance/branches/namespaced/etc/appmap.xml ============================================================================== --- (empty file) +++ z3/deliverance/branches/namespaced/etc/appmap.xml Wed Jul 5 20:50:26 2006 @@ -0,0 +1,22 @@ + +
+