Seite 1 von 2

PyBlogger, guter Code, schlechte Code... das ist hier die...

Verfasst: Samstag 29. November 2008, 13:16
von Crazed
Ganz abgesehen von der [un]witzigen Anspielung im Thread Titel geht es hier noch um was anderes. Ein Freund und Ich habe durch dieses Forum Pflock kennen gelernt.

Pflock ist ein Werkzeug um einen statischen Blog, bzw. viel eher auch, statische Webseiten mittels Python zu generieren. Wir fanden diesen Aspekt sehr interessant, leider gefiel uns die gewählte MarkUp Sprache nicht sonderlich (ReST) und Code-Intern hätten wir einige Dinge anders realisiert.

Nach ein bisschen Arbeit, mehreren Male herumprobieren, Code - Umstrukturierung usw. sind wir dann zu folgenden Ergebnis gekommen:

http://paste.pocoo.org/show/v9x59aXnL0rpuUVceeJP/

Wir haben es probiert so minimalistisch und logisch wie möglich zu halten. Der minimalistische Aspekt ist vielleicht ganz gut gelungen, aber bei dem logischen haben ich noch so meine Zweifel. Die größten Sorgen bereiten mir die Effizienz und die Schönheit bzw. Strukturierung des Codes.

Aber erstmal:
Was passiert überhaupt?
Man hat idealerweise 2 Templates. Ein blog.template und ein post.template. Jeder dieser Templates ist genau genommen eine HTML Seite mit spezillen keywords wie z.b

$author, $title, $content etc.

Beide Templates besitzen nun z.b $content. Im Falle des post.template, wir content mit dem Inhalt des Blogeintrages ersetzt und beim dem blog.template wird z.b $content mit dem Inhalt aller bereits bearbeiteten post.templates ersetzt. (Das wäre dann die index.html)

D.h erst werden die post.template "gerendered" und danach das blog.template das die "gerenderten" post.templates brauch.

Die daten für die Posts werden aus config['input'] geholt, und sind genau genommen Python dateien. Wer sich das ganze genau anschauen will hier gibt es eine zip datei:

http://www.speedyshare.com/824777223.html

Ich hoffe jemand kann mir ein paar Tipps bezüglich des allgemeinen Stils, der Strukturierung, Effizienz und der logischen Aspekte bezüglich des Codes geben.

Ich bin gerne offen für Sachen die man besser machen kann.

MfG,
CracKPod

Verfasst: Samstag 29. November 2008, 13:33
von fred.reichbier
Hallo,

beim ersten Durchsehen ist mir aufgefallen, dass das hier ziemlich oft vorkommt:

Code: Alles auswählen

        for key, value in assignments.iteritems():
            self.assignments[key] = value
Wäre da nicht self.assignments.update(assignments) angenehmer?

Und zu den Templates mit $blablubb: Python hat sowas schon dabei ;)

Gruß,

Fred

Verfasst: Samstag 29. November 2008, 14:33
von Crazed
Danke! Offensichtlich zwei Sachen die ich einfach nicht kannte. Falls dir noch was auffällt sag mir bitte bescheid. :) Die anderen natürlich auch.

Verfasst: Samstag 29. November 2008, 15:03
von veers
Nimm eine richtige Template-Engine wie Jinja oder Mako. Zu _generate_title, würde ich mit einer regex machen. Oder zumindest string.ascii_letters verwenden ;)

- Jonas

Verfasst: Samstag 29. November 2008, 15:15
von Crazed
Danke veers! Das es so ausgereifte Template Engines gab wusste ich ja noch gar nicht. Aber wo genau ist der Vorteil gegenüber meiner Template klasse?

Und wenn ich eine nehme, welche dann? Es sahen jetzt die beiden die du gennant hast auf den ersten Blick echt geil aus, aber viel Unterschied gibt es jetzt auf den ersten Blick nicht

Code: Alles auswählen

>>> from jinja2 import Template
>>> template = Template('Hello {{ name }}!')
>>> template.render(name='John Doe')
u'Hello John Doe!'

Code: Alles auswählen

from mako.template import Template
print Template("hello ${data}!").render(data="world")
Und außerdem gibt es noch:
  • Myghty
    Cheetah
    Genshi
    Django
    Kid
Laut der mako homepage.

Jetzt hast du mich in eine Ecke getrieben aus die ich ganz schlecht wieder rauskomme. Ich hatte schon immer ein Problem mit Entscheidungen...

Verfasst: Samstag 29. November 2008, 15:40
von veers
Ich persönlich mag Jinja2 und Mako. Diese unterstützen unter anderem Vererbung von Templates, Schleifen, Template Tags, includes, filter ... ;)
Dann gibt es noch die XML basierten Template Systeme wie Genshi, welche mich persönlich aber weniger ansprechen.

Mit Jinja machst du wohl nichts falsch. Wenn dich etwas daran nervt kannst du dir ja immer noch die Alternativen ansehen. ;)

- Jonas

Verfasst: Samstag 29. November 2008, 15:48
von Crazed
An Jinja stört mich ein bisschen das man dort was kompilieren muss damit es schnell wird, ist das bei Mako auch so?

Verfasst: Samstag 29. November 2008, 15:52
von lunar
Vollwertige Template-Engines beherrschen Schleifen und allerhand verschiedener Filter. Dadurch kann man die Aufbereitung für die Präsentation komplett in die Vorlage auslagern, und muss z.B. Zahlenformatierungen oder Wiederholungen nicht im Quellcode ausführen.

Kid und Myghty sind veraltet, Django ist nur ein schlechteres Jinja (und noch dazu an Django gekoppelt). Genshi ist XML basiert, was das Editieren der Templates zu einer sehr undankbaren Aufgabe macht. Jinja2 und Mako sind beide mächtig, schnell und komfortabel, der größte Unterschied liegt in der Syntax. Da mir die Mako-Syntax nicht so gefällt, habe ich mich für Jinja2 entschieden. Ein Vorteil an Jinja2 ist übrigens, dass man die Syntax konfigurieren kann, um sie dem eigenen Markup anzupassen.

Btw, was habt ihr eigentlich gegen ReST? Das ist doch so ziemlich das beste Text-Markup, was es gibt ...

Verfasst: Samstag 29. November 2008, 16:11
von Crazed
Oke, Ich werde Jinja2 nehmen und mir Rest nochmal genauer anschauen. Vielen Dank für die Hilfe bis jetzt. Aber eine Frage am Rande:

Was könnt ihr über die Qualität meines Codes sagen? Würde mich wirklich interessieren.

EDIT: Was mir wirklich noch interessiert, muss Mako auch irgendwas kompilieren?
EDIT2: Und wenn ich jetzt sowas mit Jinja2 oder Mako und ReST mache, wie gestalte
ich das am besten Objekt Orientiert? Hat jemand eine Idee, mein letzter Versuch war irgendwie nicht _der_ Erfolg.

Verfasst: Samstag 29. November 2008, 19:51
von veers
Das musst du nicht selbst kompilieren. Das machen Jinja/Mako transparent für dich. Du siehst eigentlich nur das es danach schneller ist ;)

Und zu ReST, entweder schon kompiliert übergeben oder einen rest Filter schreiben. Gibt es eventuell schon ;)

Verfasst: Sonntag 30. November 2008, 00:07
von lunar
Crazed hat geschrieben:EDIT: Was mir wirklich noch interessiert, muss Mako auch irgendwas kompilieren?
Afaik nicht.
EDIT2: Und wenn ich jetzt sowas mit Jinja2 oder Mako und ReST mache, wie gestalte ich das am besten Objekt Orientiert? Hat jemand eine Idee, mein letzter Versuch war irgendwie nicht _der_ Erfolg.
Sphinx – das Programm, mit dem die neue Python-Dokumentation erstellt wurde – parst das Markup in einem Baum (das erledigt docutils), führt verschiedene Modifkationen an diesem Baum aus, erzeugt dann daraus HTML, und übergibt das dann an das Template, welches das HTML mit Navigationsstruktur versieht.

Die Wahl der Template-Engine würde ich nicht am Kompilieren festmachen. Bei einer statischen Erzeugung der HTML-Dateien ist die Geschwindigkeit des Rendering-Prozesses sowieso eher irrelevant. Bei Sphinx beispielsweise ist der Unterschied zwischen Jinja und Jinja2 beim Buildprozess auch nicht wirklich spürbar, obwohl Jinja2 eigentlich schneller ist.

Verfasst: Sonntag 30. November 2008, 01:13
von veers
lunar hat geschrieben:
Crazed hat geschrieben:EDIT: Was mir wirklich noch interessiert, muss Mako auch irgendwas kompilieren?
Afaik nicht.
Tut es, jedoch auch transparent.

Verfasst: Sonntag 30. November 2008, 01:49
von Crazed
Ja, weil mir halt einfach Sorgen macht, falls ich irgendwann unser Projekt veröffentlichen sollte, das sich nicht jeder erst Jinja2 installieren und ggf. kompilieren muss. Viele Laien werden dann auch nicht wissen (wie ich es mal nicht wusste) das man erst die Python Quellcode Dateien haben muss so wie einen funktionieren Compiler.

Habe aber glaube die Lösung eh gefunden, dank bbfreeze. Ich freeze nicht die ganze Applikation sondern nur Jinja2 mehr oder weniger und kann daraus dann einfach ein sehr kleines, portables site-package erstellen. Sieht dann so aus:

http://www.speedyshare.com/438370518.html

Wäre nett wenn jemand mal probieren könnte Jinja damit zu benutzen. Einfach ein Python script erstellen das jinja2 importiert und den Ordner innerhalb der zip Datei in das gleiche Verzeichnis wie das erstellte Script packen oder auch ggf. irgwo in sys.path.

CracKPod

Verfasst: Sonntag 30. November 2008, 02:16
von Leonidas
Crazed hat geschrieben:Ja, weil mir halt einfach Sorgen macht, falls ich irgendwann unser Projekt veröffentlichen sollte, das sich nicht jeder erst Jinja2 installieren und ggf. kompilieren muss. Viele Laien werden dann auch nicht wissen (wie ich es mal nicht wusste) das man erst die Python Quellcode Dateien haben muss so wie einen funktionieren Compiler.
Die installieren einfach Jinja mit ihrem Paketmanager. Oder Mako.

Verfasst: Sonntag 30. November 2008, 10:13
von sma
Statt der eigenen Klasse Template kannst du string.Template benutzen. Mako und co. brauchst du nur, wenn du auch deren Features haben willst, da die Abhängigkeit von anderen Modulen (gerade wenn man im Fall von Jinja noch einen C-Compiler installiert haben muss, wie ich das aus der Diskussion verstanden hatte) ein Preis ist, der auch Wert sein muss, bezahlt zu werden. Mir wäre er zu hoch.

Wenn du's selbst machen willst, schlage ich folgende Implementierung vor:

Code: Alles auswählen

class Template(object):
    def __init__(self, template):
        self.template = template

    def substitude(self, mapping):
        return re.sub(
            r'\$(?:\{([^}]+)\}|(\w+)|(\$))', 
            lambda m: '$' if m.group(3) else mapping[m.group(m.lastindex)],
            self.template)
`_generate_title` ist viel zu komplex. Würdest du zuerst `lower()` aufrufen, könntest du dir schon mal alle Großbuchstaben in `legal` sparen. Auch könntest du einen String benutzen. Die Zuweisung in der Schleife ist alles nur noch elegant. Ich würde `filter` (siehe unten) empfehlen.

Erbe nicht von Template. Das ist kein guter Stil. Und führe nicht alles im Konstruktor aus - das ist ebenfalls schlechter Stil. Übergib ein Template als Argument, nicht nur den String.

Benutze nicht `file`, sondern einfach `open`. Wozu brauchst du eigentlich `imp.load_source`? Das erscheint mir ein komischer Weg zu sein, die Blogartikel einzulesen. Das `post_template` musst du in jedem Fall nicht jedes Mal in der Schleife erneut laden.

Code: Alles auswählen

class Post(object):
    def __init__(self, template, post):
        self.template, self.post = template, post
    
    def render(self):
        self.template.substitude({
            'title': self.post.title,
            'author': self.post.author,
            ...
        })

def generate_title(t):
    valid = lambda c: c.isalpha() or c in '-_'
    return filter(valid, t.lower().replace(' ', '_'))

class Blog(object):
    def __init__(self, template, posts):
        self.template, self.posts = template, posts
    
    def render(self):
        self.template.substitude({'content': '\n'.join(posts)})

def render(config):
    ...
    blog = Blog(
        Template(open(config['blog_template']).read()),
        [post.render() for post in posts])
    open(..., "w").write(blog.render())
Stefan

Verfasst: Sonntag 30. November 2008, 12:13
von Crazed
@Leonidas, genau das will aber ich aber nicht. Ich möchte das es einfach "out-of-the-box" funktioniert ohne irgendwas zusätzliches installieren zu müsen.

@sma, Ich kenne mich leider kaum mit RegEx aus, wäre eventuell mal ganz gut wenn ich
mich ein bisschen mit der Materie auseinandersetze.

"Erbe nicht von Template. Das ist kein guter Stil." -> Wozu kann man dann erben? bzw. in welcher Situation ist es angebracht? Ich hielt das für sehr elegant.

"Benutze nicht `file`, sondern einfach `open`." -> Wo ist der Vorteil? Meines erachtens machen die beiden Funktionen fast das gleiche.

"Wozu brauchst du eigentlich `imp.load_source`?" -> Ich habe eine komische Art entwickelt blog-posts zu schreiben. Diese sind genau genommen einfach Python dateien in denen ein paar Variablen definiert werden, wie z.b content, author, date und title

"Übergib ein Template als Argument, nicht nur den String." -> Tue ich das nicht bereits?

Ich glaube fast ich packe einfach den nur knapp den Ordner mit der Jinja distribution einfach mit darein und benutze jinja2. Warum denn auch nicht? Dort gibt es viele und sehr komfortable features die ich gebrauchen kann. Außerdem hat man innerhalb des Templates viel mehr freiheit.

Trotzdem vieelen Dank. Ich finde deinen Coding Stil übrigens sehr hübsch und werde mich an einigen von deinen Ideen halten.

Wenn ich mich etwas doof anstelle, tut mir das Leid ^^.

Verfasst: Sonntag 30. November 2008, 13:14
von Leonidas
Crazed hat geschrieben:@Leonidas, genau das will aber ich aber nicht. Ich möchte das es einfach "out-of-the-box" funktioniert ohne irgendwas zusätzliches installieren zu müsen.
Wo ist der Vorteil?
Crazed hat geschrieben:"Benutze nicht `file`, sondern einfach `open`." -> Wo ist der Vorteil? Meines erachtens machen die beiden Funktionen fast das gleiche.
``open()`` ist zum Öffnen von Dateien gedacht, ``file`` zum Erben um ``file``-like-Objekte zu erzeugen.

Verfasst: Sonntag 30. November 2008, 13:24
von Y0Gi
Kid kannst du mal getrost vergessen. Es ist einer der Vorgänger von Genshi und teilweise broken by design. Ich habe über die Unterschiede seinerzeit mal einen kleinen Beitrag auf meiner Website geschrieben, der über das Genshi-Wiki zu finden ist.


ReST benutze ich für die editierbaren Inhalte meiner persönlichen Website ebenfalls mit großer Zufriedenheit und habe damit Markdown abgelöst. Auf einer anderen benutze ich wiederum gewollt Markdown (natürlich mit diversen eigenen, anwendungsspezifischen Erweiterungen) statt ReST, da dort auch weniger technisch versierte und geübte Benutzer Inhalte schreiben und mit der Mächtigkeit von ReST doch ein wenig überfordert wären.

Verfasst: Sonntag 30. November 2008, 15:38
von lunar
veers hat geschrieben:
lunar hat geschrieben:
Crazed hat geschrieben:EDIT: Was mir wirklich noch interessiert, muss Mako auch irgendwas kompilieren?
Afaik nicht.
Tut es, jedoch auch transparent.
Mmmh, reden wir hier beide über das gleiche Kompilieren? Jinja2 compiliert eine _speedups.so, beim – zugegebenermaßen groben – Durchsehen des Mako-Eggs hab ich aber keine Binaries entdecken können.

Verfasst: Sonntag 30. November 2008, 16:42
von str1442
"Benutze nicht `file`, sondern einfach `open`." -> Wo ist der Vorteil? Meines erachtens machen die beiden Funktionen fast das gleiche.
File ist eine Klasse die ein File Objekt repräsentiert, open eine Funktion, die File benutzt. Im Grunde ist es egal, welche du nutzt, da die __init__ von file die gleichen Argumente entgegennimmt wie open, aber die Dokumentation empfiehlt die Nutzung von open(). Warum auch immer.
"Erbe nicht von Template. Das ist kein guter Stil." -> Wozu kann man dann erben? bzw. in welcher Situation ist es angebracht? Ich hielt das für sehr elegant.
Template teilt zwar mit allen Unterklassen die Eigenschaften, es ist aber rein logisch ein wenig eigenartig. Das ist in etwa so, als würdest du beim Versuch des Darstellens eines Raumes die Objekte Stuhl, Tisch und Tür von "Holz" erben lassen.

Ich würde in vorliegendem Fall das mittels Komposition lösen, dh alles was mit Templates zu tun hat in einer wohlgeformten Template Klasse unterbringen, und jeder "Page" dann einde Instanz von "Template" geben. Zumindest so auf den ersten Blick.

Vererben sollte man imho wirklich nur Dinge, die zueinander gehören. Sobald du mittels Vererbung die eigentliche Logik der Superklasse in zig andere Klassen schleppst, die so direkt mit einem Template nichts zu tun haben, brichst du die Kapselung.