Mini-Template-Engine

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Wenn es Micro-Webrahmenwerke gibt, muss es auch Mini-Template-Engines geben. Meine ist noch ein bisschen zu groß und ich bin nicht 100% von dem Ansatz überzeugt, aber ich habe mal versucht, etwas wie Djangos Engine in 100 Zeilen zu pressen (ohne die ganzen eingebauten Tags und Filter): http://gist.github.com/140888

Die Engine kann so etwas verarbeiten:

Code: Alles auswählen

Beispiel:
{% if len(items) %}
    {% for i in items %}
        {% if i|odd %}{{ i }}{% else %}-{% end %}
    {% end %}
{% end %}
Im Gegensatz zu Django ist alles, was wie Variablen aussieht, echter Python-Code, der mit `eval` verarbeitet wird. Filter realisiere ich durch Überladen von `|`. Geschwindigkeitsrekorde wird die Engine auch nicht aufstellen, baue ich doch überall Strings zusammen. Und Fehler fange ich schon mal gar nicht ab...

Stefan
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Statt

Code: Alles auswählen

{% if len(items) %}
reicht doch auch

Code: Alles auswählen

{% if items %}
oder übersehe ich da etwas?

Edit: Warum hast du aus Zeile 81 kein lambda gemacht?
Edit2: Warum kein re.finditer in Zeile 7?
stuhlbein
User
Beiträge: 89
Registriert: Freitag 9. Januar 2009, 16:08

Coole sache, vorallem da es wirklich gut in einige Mini-frameworks passen würde. ;)

Leider hat es den ersten Test-durchlauf nicht bestanden, da ich anscheinend was falsch machte?

Der code:

Code: Alles auswählen

import os
import minidjango

if __name__ == '__main__':
    print minidjango.Template("""
        {% for key, value in environ_items %}
            {{ key }} := "{{ value }}"
        {% end %}
    """).render({
            'environ_items': os.environ.iteritems
        })
Die Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "./md_test.py", line 11, in <module>
    """).render({
  File "/home/nil/Desktop/minidjango.py", line 7, in Template
    return Block(None, iter(re.findall(tokens, source)))
  File "/home/nil/Desktop/minidjango.py", line 38, in __init__
    body.append(tags[parts[0]](parts, tokens))
  File "/home/nil/Desktop/minidjango.py", line 68, in __init__
    assert 'in' == parts[2]
AssertionError
;)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Man sieht an der Fehlermeldung recht deutlich dass es an der 3 Stellen in ``{% for %}`` ein ``in`` erwartet, somit wird also kein Tuple-Unpacking unterstützt.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Wäre es nicht einfacher, den kram Zeile für Zeile mit ein wenig re-magie in nativen python-code umzuwandeln und das dann komplett in den eval zu quetschen? Beispiel:

Code: Alles auswählen

%for item in list:
  { item | repr }
%end
wird zu

Code: Alles auswählen

def render(list):
  #%for item in list:
  for item in list:
    #  { item | repr }
    yield "  "
    yield repr(item)
    yield "\n"
  #%end
Man müsste stumpf alles mit % als python code übernehmen, anhand der %if/for/while und %end Instruktionen die Einrückung mit zählen und alle Strings dazwischen mit yield ohne Änderung zurück geben. Das wäre denke ich auch deutlich performanter. Ich glaub das bastel ich mal die Tage.
Bottle: Micro Web Framework + Development Blog
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Naja, aber da verlierst du ja komplett den Sandbox- bzw. Unabhängigkeitsfaktor.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Du kannst ja immer noch kontrollieren, was du als python Ausdruck zu lässt, aber ich finde es umständlich, die Python-Sprache in Python-Klassen abzubilden und dann einzeln aus zu führen O.o Sandbox erreichst du später mit beschnittenen globals() und locals(). Was du mit Unabhängigkeit meinst, weis ich nicht so recht.

Bei Templates gibt es ja generell einen Glaubenskrieg: Manche trauen den Template-Schreibern und manche trauen ihnen nicht. Ich gehöre zu der ersten Fraktion.
Bottle: Micro Web Framework + Development Blog
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Ich auch, ich finde aber, man sollte den User stets davor schützen, alles kaputtzuhauen ;-)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

@deron, das `len` sollte nur zeigen, dass eingebaute Funktionen funktionieren. Zu Zeile 81: Ich ziehe ein `def a(): return 1` einem `a = lambda: 1` auf Grundebene vor, selbst wenn es länger ist. Zu Zeile 7: `finditer` liefert match-Objekte, ich wollte aber die Strings selbst. Ich hätte `(m.group() for m in re.finditer(...))` benutzen können, aber das ist mehr und Speicherplatz war mir egal.

@Bitfish, Tupelzuweisung habe ich einfach nicht implementiert. Hatte dran gedacht und wäre nicht weiter schwer, müsste nur ein bisschen ändern, wie ich Tags parse.

@Defnull, der klassische Ansatz wie Javas JSPs oder Rubys ERBs funktionieren ist, das Programm quasi umzukrempeln und aus dem statischen Text mit ein bisschen Programmcode viel Programmcode mit ein paar zusätzlichen prints (o.ä.) zu machen. Ob man's dadurch kürzer hinbekommt, kann ich nicht überblicken. Die Mako-Syntax mit dem `%` wollte ich ursprünglich umsetzen, doch das wurde komplizierter zu parsen. Außerdem wollte ich meinen bereits existierenden Textmate-Mode für Django-Templates weiter verwenden. Ich bin auf deine Lösung gespannt.

Wenn man wirklich nur `if` und `for` braucht, ist es wahrscheinlich einfach, egal, ob man nun Djangos oder Makos Syntax realisieren will. Mein Verfahren ist generischer. Vielleicht zu generisch.

Ich könnte mir übrigens vorstellen, dass man auch HAML in < 100 Zeilen verarbeiten kann. Das geht eigentlich recht gut mit Pythons Einrückung zusammen, funktioniert dann aber natürlich nur für XHTML, nicht für beliebige Texte.

@Dauberbaustelle, ich bin da bei Defnull und in der Gruppe der Leute, die Template-Schreibern (nämlich mir) trauen. Und mit einem Template, in dem ich Python-Code aufrufen kann, kann ich nicht mehr oder weniger "kaputthauen" als mit einem Python-Programm selnbst.

Irgendwie nagt aber bei mir im Hinterkopf, dass man Templates noch anders angehen kann und vielleicht sollte. Und wenn nur, um anders zu sein. Dazu demnächst mehr.

Stefan
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

sma hat geschrieben:Ich bin auf deine Lösung gespannt.
Ich war schon fleißig :) http://www.python-forum.de/topic-19500.html
Es ist sogar schon in Bottle integriert: http://www.python-forum.de/post-141198.html
Bottle: Micro Web Framework + Development Blog
Antworten