Colubra XML Template

Code-Stücke können hier veröffentlicht werden.
Antworten
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Zu allererst. NICHT VERWENDEN.
Das ist nur ein Testprojekt von mir, da hab ich mich nur mit der XML Syntax und dem Parsern derjenigen auseinandergesetzt.

Aber vielleicht kann ja einer was daraus lernen ^^

colubra.py

Code: Alles auswählen

# -*- coding: utf-8 -*-

from xml.parsers import expat
from xml.sax import saxutils

class PythonWriter:

    one_line_tags = ['img', 'hr', 'br', 'meta', 'link']

    def __init__(self):
        self.doc = []
        self.level = 2
        self.outdent_tags = []
        self.level_indention = '    '

    def header(self):
        header_data = [
            (0, '# -*- coding: utf-8'),
            (0, 'try:'),
            (1, 'from cStringIO import StringIO'),
            (0, 'except:'),
            (1, 'from StringIO import StringIO'),
            (0, 'from xml.sax import saxutils'),
            (0, ''),
            (0, 'class CulebraTemplate:'),
            (1, 'def __init__(self, namespace, output=None):'),
            (2, 'self.__namespace = namespace'),
            (2, 'self.__do_output = False'),
            (2, 'if output is None:'),
            (3, 'self.__result = StringIO()'),
            (3, 'self.__do_output = True'),
            (2, 'else:'),
            (3, 'self.__result = output'),
            (1, ''),
            (1, 'def __call__(self):'),
            (2, 'self.__run__()'),
            (2, 'if self.__do_output:'),
            (3, 'return self.__result.getvalue()'),
            (1, ''),
            (1, 'def __run__(self):'),
            (2, 'for key in self.__namespace:'),
            (3, 'globals()[key] = self.__namespace[key]'),
            (2, 'try: del key'),
            (2, 'except NameError: pass')
        ]
        for line in header_data:
            self.doc.append(line)

    def translate_var(self, var):
        parts = str(var).split('.')
        result = parts[0]
        for part in parts[1:]:
            result += '[\'%s\']' % part.encode('string-escape')
        return result

    def write_line(self, data):
        data = str(data).encode('string-escape')
        self.doc.append((self.level, 'self.__result.write(\'%s\')' % data))

    def write_var(self, data):
        var = self.translate_var(data)
        strvar = '{%s}' % str(data).encode('string-escape')
        self.doc.append((self.level, 'try:'))
        self.doc.append((self.level + 1, 'self.__result.write(%s)' % var))
        self.doc.append((self.level, 'except:'))
        self.doc.append((self.level + 1, 'self.__result.write(\'%s\')' % strvar))
        
    def write_var_quoted(self, data):
        var = self.translate_var(data)
        strvar = saxutils.quoteattr('{%s}' % str(data).encode('string-escape'))
        self.doc.append((self.level, 'try:'))
        self.doc.append((self.level + 1, 'self.__result.write(saxutils.quoteattr(%s))' % var))
        self.doc.append((self.level, 'except:'))
        self.doc.append((self.level + 1, 'self.__result.write(\'%s\')' % strvar))
        
    def write_replace(self, variables, data):
        data = str(data).encode('string-escape')
        self.doc.append((self.level, '__tmp_output = \'%s\'' % data))
        self.doc.append((self.level, '__replacement_table = {'))
        for var in variables.split(','):
            p = var.split(' with ', 1)
            if len(p) == 1:
                _search = str(p[0].strip()).encode('string-escape')
                _replace = self.translate_var(p[0].strip())
            else:
                _search = str(p[0].strip()).encode('string-escape')
                _replace = self.translate_var(p[1].strip())
            
            self.doc.append((self.level + 1, '\'%s\': %s,' % (_search, _replace)))
        self.doc.append((self.level, '}'))
        self.doc.append((self.level, 'for __search, __replace in __replacement_table.items():'))
        self.doc.append((self.level + 1, '__tmp_output = __tmp_output.replace(\'$%s\', __replacement_table[\'%s\'])' % (_search, _search)))
        self.doc.append((self.level, 'self.__result.write(__tmp_output)'))
    
    def write_tag(self, name, attributes):       
        if 'py:if' in attributes:
            self.doc.append((self.level, 'if %s:' % attributes['py:if'].strip()))
            del attributes['py:if']
            self.outdent_tags.append(name)
            self.level += 1
                    
        self.write_line('<' + name)
        
        py_for = False
        if 'py:for' in attributes:
            py_for = attributes['py:for']
        
        if 'py:attrib' in attributes:
            replaces = attributes['py:attrib'].split(',')
            for item in replaces:
                p = item.split(' as ', 1)
                _var = p[0].strip()
                _as = p[1].strip()
                self.write_line(' %s=' % _as)
                self.write_var_quoted(_var)
                if _as in attributes:
                    del attributes[_as]
            del attributes['py:attrib']
        for key in attributes:
            self.write_line(' %s=%s' % (key, saxutils.quoteattr(attributes[key])))
        
        if name in self.one_line_tags:
            self.write_line(' />')
        else:
            self.write_line('>')
           
        if py_for:
            p = py_for.split(' as ')
            _as = p[1].strip()
            _var = p[0].strip()
            self.doc.append((self.level, 'for %s in %s:' % (_as, _var)))
            self.outdent_tags.append(name)
            self.level += 1
    
    def write_end_tag(self, name):
        try:
            last = self.outdent_tags[-1]
            if last == name:
                self.outdent_tags.pop()
                self.level -= 1
        except IndexError:
            pass
        if not name in self.one_line_tags:
            self.write_line('</%s>' % name)

    def get_data(self):
        result = ''
        for indent, line in self.doc:
            result += self.level_indention * indent + line.strip() + '\n'
        return result


class Parser:

    def __init__(self, template):
        self.parser = expat.ParserCreate()
        self.parser.StartElementHandler = self.start_element
        self.parser.EndElementHandler = self.end_element
        self.parser.CharacterDataHandler = self.cdata
        self.compiler = None
        self.template = template
        
        self.cdata_mod = None

    def start_element(self, name, attrs):
        if 'py:insert' in attrs:
            self.cdata_mod = ('insert', attrs['py:insert'])
            del attrs['py:insert']
        elif 'py:replace' in attrs:
            self.cdata_mod = ('replace', attrs['py:replace'])
            del attrs['py:replace']
        
        self.compiler.write_tag(name, attrs)

    def end_element(self, name):
        self.compiler.write_end_tag(name)

    def cdata(self, data):
        if not self.cdata_mod is None:
            if self.cdata_mod[0] == 'insert':
                self.compiler.write_var(self.cdata_mod[1])
            if self.cdata_mod[0] == 'replace':
                self.compiler.write_replace(self.cdata_mod[1], data)
            self.cdata_mod = None
        else:
            self.compiler.write_line(data)

    def parse(self):
        self.compiler = PythonWriter()
        self.compiler.header()
        self.parser.Parse(self.template)
        return self.compiler.get_data()


###############################################################################
# Simple Access

def parse(filename):
    t = file(filename + '.xml').read()
    p = Parser(t)
    return p.parse()

def compile(filename):
    import os
    import py_compile
    
    code = parse(filename)
    filename += '.py'
    file(filename, 'w').write(code)
    py_compile.compile(filename)
    os.unlink(filename)

def execute(filename, namespace):
    try:
        module = __import__(filename, locals(), globals(), ['CulebraTemplate'])
    except ImportError:
        compile(filename)
        module = __import__(filename, locals(), globals(), ['CulebraTemplate'])
    return module.CulebraTemplate(namespace)()
test.py

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import culebra

namespace = {
    'page': {
        'title': 'Demonstation Page'
    },
    'navigation': [
        {'href': '#', 'title': 'current page'},
        {'href': 'index.html', 'title': 'overview'},
        {'href': 'about.html', 'title': 'about'}
    ]
}

print culebra.execute('demo', namespace)
demo.xml

Code: Alles auswählen

<?xml version="1.0" encoding="utf-8"?>
<html>
    <head>
        <title py:replace="title with page.title">CulebraDemoTemplate :: $title</title>
    </head>
    <body>
        <h1 py:insert="page.title">%s</h1>
        <ul py:for="navigation as item">
            <li><a py:attrib="item.href as href, item.title as title" py:insert="item.title">%s</a></li>
        </ul>
        <div id="footer" py:insert="footer">%s</div>
    </body>
</html>
Ausgabe sieht dann etwa so aus:

Code: Alles auswählen

<html>
    <head>
        <title>CulebraDemoTemplate :: Demonstation Page</title>
    </head>
    <body>
        <h1>Demonstation Page</h1>
        <ul>
            <li><a href="#" title="current page">current page</a></li>

            <li><a href="index.html" title="overview">overview</a></li>

            <li><a href="about.html" title="about">about</a></li>
        </ul>
        <div id="footer">{footer}</div>
    </body>
</html>
Zuletzt geändert von mitsuhiko am Freitag 9. Dezember 2005, 15:25, insgesamt 1-mal geändert.
TUFKAB – the user formerly known as blackbird
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Was soll dass denn werden? Eine Kid-ähnliches Templetingsystem?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Leonidas hat geschrieben:Was soll dass denn werden? Eine Kid-ähnliches Templetingsystem?
Das soll gar nichts werden. Ich wollte mich mit Expat auseinandersetzen und das ist rausgekommen. Deswegen poste ich es ja in Codesnippets und nicht im Showcase :wink:
TUFKAB – the user formerly known as blackbird
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

ARGH. das syntax highlighting macht den code kaputt. Wers testen will:
1.) beitrag quoten
2.) eintrag rauskopieren
3.) ein datei einfügen
:wink:
TUFKAB – the user formerly known as blackbird
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Sieht für mich wie eine Mischung aus simpleTAL mit SpyTee aus ;)

Wenn du dich mit Templating auseinander setzt, solltest du dir auf jeden Fall mal simpleTAL anschauen!

Hier ein paar Links, die du vielleicht schon kennst:
http://www.python-forum.de/viewtopic.php?t=4323
http://www.python-forum.de/viewtopic.php?t=4242
http://www.python-forum.de/viewtopic.php?t=4256
http://www.python-forum.de/viewtopic.php?t=4231

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

jens hat geschrieben:Sieht für mich wie eine Mischung aus simpleTAL mit SpyTee aus ;)

Wenn du dich mit Templating auseinander setzt, solltest du dir auf jeden Fall mal simpleTAL anschauen!
verzichte, dass war nur ein Test ^^
Momentan schreib ich an einem Python 2 Javascript Konverter.
TUFKAB – the user formerly known as blackbird
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

blackbird hat geschrieben:Momentan schreib ich an einem Python 2 Javascript Konverter.
Hm! Da bin ich ja mal gespannt! Ich nehme an für AJAX???

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Wie wärs mit CrackAJAX?
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Leonidas hat geschrieben:Wie wärs mit CrackAJAX?
AHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.
Warum erfahre ich erst jetzt davon? Na egal. Ich bin eh schon weiter :)
TUFKAB – the user formerly known as blackbird
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

blackbird hat geschrieben:Warum erfahre ich erst jetzt davon?
Zu wenig Daily Python URL gelesen? :wink:

Dein Projekt ist schon interessant, so habe ich auch nicht wirklich Lust JavsScript selbst zu schreiben, auch nicht mit MochiKit.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Leonidas hat geschrieben:Dein Projekt ist schon interessant, so habe ich auch nicht wirklich Lust JavsScript selbst zu schreiben, auch nicht mit MochiKit.
Deswegen hab ich ja damit angefangen. Aber zuerst wollte ich nur eine javascript bridge zu python funktionen machen. Gestern musste ich dann ein pyc compilieren und habe durch zufall compiler.ast gefunden.
Dann lag das schon fast auf der Hand :-)

Der Parser ist jetzt fertig, ich lege jetzt noch die python objekte als javascript objekte ab. Momentan ist aber nur String zu 70% fertig und einige builtin functionen wie len() und so.
TUFKAB – the user formerly known as blackbird
Antworten