[Zope-dev] Python-Document proof of concept code

Martijn Faassen M.Faassen@vet.uu.nl
Tue, 15 Jun 1999 19:10:38 +0200


Hi there,

Since I hear Python scripts inside the Zope system are in their
Inception phase now, I thought I'd contribute with some ideas.

What I'm thinking about is how to emulate the DTML experience in Python.
DTML is nice in that it provides a fairly simple interface into the Zope
database, but less nice because fancy things tend to become extremely
cumbersome and complicated in DTML while they wouldn't be so horrible in
Python. Current ways to use Python tend to be even more cumbersome for
various reasons (external methods not editable from the web, products
are often overkill), however. 

One other reason why Python might still remain overkill even when it's
editable from the web is the interfacing with Zope. This proof of
concept tries to emulate DTML's way to use the Zope database in Python
code. This way people won't have to learn two different systems and run
into trouble when switching back and forth from DTML and Python
documents.

The proof of concept code does *not* tie into any Zope system, it's just
an attempt to demonstrate my idea of DTML-like constructs in Python.
There are various caveats, such as the HTML that's produced currently
being all rendered to one line. That can be solved by adding \n in
places, but perhaps there's a nicer way still.

Enjoy reading the code. :)

Regards,

Martijn

---- The actual code ----
# Just a proof of concept only! Not based on any actual Zope code, just
# tries to imitate it vaguely. :)
  
from array import array

class Error(Exception):
    """Simplistic error.
    """
    pass

class PyDocument:
    """A PyDocument is like a DTML document, but in reverse. Program
logic
    with embedded HTML, instead of the other way around.

    The idea is not to replace current DTML documents, which work fine
as
    a reporting language, but to provide a starting place for Python
    Script Documents in Zope.
    """
    def var(self, name):
        """Like var tag, name attribute only. No expr attribute
supported.
        """
        obj = self.namespace.get(name, None)
        if obj != None:
            self.buffer.fromstring(obj(self.namespace))
        else:
            raise Error, "The name %s does not exist in the namespace."
% name

    def call(self, name):
        """Like call tag, name attribute only.
        """
        obj = self.namespace.get(name, None)
        if obj != None:
            obj(self.namespace)
        else:
            raise Error, "The name %s does not exist in the namespace."
% name
        
    def HTML(self, data):
        """A way to include HTML code.

        (or any text, perhaps rename this method to 'raw' or something).
        """
        self.buffer.fromstring(data)

    def __call__(self, namespace):
        """Render this PyDocument.
        """
        self.namespace = namespace
        self.buffer = array('c', "")
        self.script()
        return self.buffer.tostring()

    def script(self):
        """Override this method for the actual Python Document.
        """
        pass

class StringValue:
    """A var tag calls things to render them. This renders by just
returning
    the string.
    """
    def __init__(self, value):
        self.value = value

    def __call__(self, namespace):
        return self.value

#####################################################################
# example code

class standard_html_header(PyDocument):
    """A simple standard_html_header.
    """
    def script(self):
        self.HTML('''<html><title>''')
        self.var(name="title")
        self.HTML('''</title><body>''')

class standard_html_footer(PyDocument):
    """A simple standard_html_footer.
    """
    def script(self):
        self.HTML('''</body></html>''')

class SomePage(PyDocument):
    """Some other page.
    """
    def script(self):
        self.var("standard_html_header")
        self.HTML('''<p>''')
        self.var("foo")
        self.HTML('''</p>''')
        self.var("standard_html_footer")
        
def demo():
    # construct a namespace
    namespace = {
        "foo": StringValue("FOO"),
        "title": StringValue("This is the title of the page"),
        "standard_html_header" : standard_html_header(),
        "standard_html_footer" : standard_html_footer(),
        }
    # create instance of SomePage class
    somepage = SomePage()
    # now call somepage with namespace to render it
    print somepage(namespace)

def test():
    demo()

if __name__=="__main__": test()