Gibt es komponentenbasierte Webrahmenwerke?

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Gibt es eigentlich Python-Web-Rahmenwerke, die komponentenbasiert sind wie z.B. Smalltalks Seaside oder Javas Wicket (oder Tapestry oder -gasp- JSF)?

Typischerweise muss man sich hier nicht selbst darum kümmern, wie man den Kontrollfluss innerhalb einer Webanwendung zerflückt, sondern arbeitet mit Callbacks oder Continuations. Letztes wird in Python nicht möglich sein, aber Wicket ist z.B. für Java-Verhältnisse schon nett.

Hier sind ein paar Beispiele.

Bei Seaside steht der Code, der eine Operation definiert, die durch einen Link (oder Button) ausgelöst werden soll, unmittelbar dort, wo auch der Link definiert wird. Das ist explizit und DRY, erspart es einem doch selbst URLs zu erfinden, sie explizit auf Controller (oder im Django-Fall auf Views) zu dispatchen und dann dort die Funktion zu implementieren. Dank Continuations wird der gesamte Zustand der Anwendung automatisch verwaltet.

Hier ist eine Komponente `Counter`, die eine Zahl anzeigt, die man In- und Dekrementieren kann.

Code: Alles auswählen

initialize
    count := 0

renderContentOn: html
    html
        heading: count;
        anchor: '++' callback: [count := count + 1];
        space;
        anchor: '--' callback: [count := count - 1]
In Python könnte das so aussehen. Python ist etwas geschwätziger und kennt keine Blöcke, daher muss ich weitere Methoden explizit definieren.

Code: Alles auswählen

class Counter(Component):
    def __init__(self):
        self.count = 0
    
    def renderContent(self, html):
        html.heading(self.count)
        html.div[
            html.anchor(callback=self.increment)["++"],
            " ", 
            html.anchor(callback=self.decrement)["--"],
        ]
    
    def increment(self):
        self.count += 1
    
    def decrement(self):
        self.count -= 1
Mehrere Counter sind auch kein Problem. Komponenten lassen sich bei Seaside schachteln:

Code: Alles auswählen

initialize
    counters := (1 to: 6) collect: [:each | Counter new]
    
renderContentOn: html
    counters
        do: [:each | html render: each]
        separatedBy: [html horizontalRule]
Würde `Counter` in Python genau wie bei Seaside funktionieren (was nicht geht, siehe unten), ginge das natürlich auch in Python:

Code: Alles auswählen

class MultiCounter(Component):
    def __init__(self):
        self.counters = [Counter() for i in range(6)]
        
    def renderContent(self, html):
        first = True
        for counter in counters:
            if first:
                first = False
            else: 
                html.horizontalRule()
            counter.renderContent(html)
Selbst modale Dialoge sind bei Seaside dank Continuations möglich. Die Methode #show: zeigt einen (gedachten - Namen und Konventionen sind ähnlich) modalen Dialog an, um eine neue Zahl einzugeben und der Kontrollfluss scheint in dieser Zeile stehen zu bleiben bis der Benutzer eine Eingabe gemacht hat.

Code: Alles auswählen

renderContentOn: html
    ...
    html anchor: 'set' callback: [
        d := Dialog prompt: 'enter new number'.
        (self show: d) ifTrue: [count := d value asInteger]
    ]
Was Wicket kann, müsste Python auch können. Wicket kombiniert Komponenten mit Templates. Es ist jedoch extrem beim Einsatz von anonymen Klassen, ein Konzept, dass es bei Python nicht gibt und erneut den Code umständlicher macht. Hier meine Python-Adaption. Modelle landen automatisch in der Session, den Rest baut Wicket jedoch für jeden Request wieder auf und simuliert so Continuations für Arme ;)

Code: Alles auswählen

class Counter(WebPage):
    def __init__(self, name):
        WebPage.__init__(self, name)
        class Count(Model):
            def __init__(self): self.count = 0
            def increment(self): self.count += 1
            def decrement(self): self.count -= 1
            def value(self): return str(self.count)
        
        count = Count()
        
        self.add(Link("incr", click=count.increment))
        self.add(Link("decr", click=count.decrement))
        self.add(Label("count", count))

Code: Alles auswählen

<h1><span wicket:id=count>0</span></h1>
<div>
  <a wicket:id="incr">++</a>
  <a wicket:id="decr">--</a>
</div>
Mehrere Counter gehen auch hier, das explizite Template wird aber lästig:

Code: Alles auswählen

class MultiCounter(WebPage):
    def __init__(self):
        for i in range(6):
            self.add(Counter("counter-%i" % i))

Code: Alles auswählen

<div wicket:id="counter-0"></div>
<hr/>
<div wicket:id="counter-1"></div>
<hr/>
<div wicket:id="counter-2"></div>
<hr/>
<div wicket:id="counter-3"></div>
<hr/>
<div wicket:id="counter-4"></div>
<hr/>
<div wicket:id="counter-5"></div>
Allgemein gilt: Kontrollfluss wird über Callbacks implizit realisiert. Komponenten lassen sich zu größeren Komponenten kombinieren. Das Rahmenwerk kümmert sich selbst darum, seinen Zustand zwischen zwei Requests zu bewahren und schafft die Illusion, ich eine habe kontinuierlich laufende zustandsbehaftete Anwendung, keine Sammlung von Webseiten.

Gibt es hier etwas Vergleichbares im Python-Umfeld?

Stefan
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

sma hat geschrieben:Letztes wird in Python nicht möglich sein
Doch, ist. Allerdings musst du dir im klaren sein, dass dein Server dann halt nicht forken darf, sonst kannst du beim zweiten Request deinen Pausierten Frame nicht wieder finden.
TUFKAB – the user formerly known as blackbird
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sma hat geschrieben:Gibt es eigentlich Python-Web-Rahmenwerke, die komponentenbasiert sind wie z.B. Smalltalks Seaside oder Javas Wicket (oder Tapestry oder -gasp- JSF)?
Was Tapestry angeht, bin ich irgendwann mal über flannel gestolpert. Scheint aber nicht fertig zu sein.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Falls du auf mit Generationen oder Threads implementierte Coroutinen anspielst - das reicht IMHO nicht für Continuations. Ich muss sie mehr als einmal durchlaufen können. Dazu muss ich sie entweder kopieren können (was AFAIK bei Python mit Frames nicht geht) oder nichts darf sich ändern, was einen 100% funktionalen Stil erfordert, was genau Generatoren und Threads nicht sind.

Habe inzwischen einen Blog-Artikel gefunden... der Autor nennt wiederholbare Continuation "multi shot" und sagt, es wäre Stackless Python notwendig - er nutzt selbst greenlets, das sind Coroutinen.

Bis auf diesem Vortrag habe ich aber sonst nichts weiter gefunden.

Stefan
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

sma hat geschrieben:Falls du auf mit Generationen oder Threads implementierte Coroutinen anspielst - das reicht IMHO nicht für Continuations. Ich muss sie mehr als einmal durchlaufen können. Dazu muss ich sie entweder kopieren können (was AFAIK bei Python mit Frames nicht geht) oder nichts darf sich ändern, was einen 100% funktionalen Stil erfordert, was genau Generatoren und Threads nicht sind.
Greenlets oder Stackless nehmen.
TUFKAB – the user formerly known as blackbird
Antworten