Seite 1 von 30

Verfasst: Sonntag 5. Juli 2009, 07:36
von Defnull
snafu hat geschrieben: Man könnte zusätzlich zum Dekorator noch `validate` als Keywordargument anbieten, welches ein Dictionary erwartet:

Code: Alles auswählen

@route('/move/:id/:x/:y', validate=dict(id=int, x=float, y=float))
def move(id, x, y):
    pass
Da finde ich den Decorator übersichtlicher. Aber wenn gewünscht, kann ich mir was direkt für den route() decorator überlegen. Direkt im Syntax ist das wie gesagt nicht so einfach, da re.match() nun mal keine Typen-Umwandlung macht.
snafu hat geschrieben:Oder die Typen in einer Liste übergeben, ähnlich wie ctypes das bei den Argumenten macht. Ist vielleicht noch etwas besser.
Das wiederum geht nicht, da die Platzhalter keine wirklich eindeutige Ordnung haben: "/archive/(:year/:month/:day|/:month/:year)"
Es muss schon nen dict sein.

Verfasst: Sonntag 5. Juli 2009, 08:00
von snafu
Defnull hat geschrieben:"/archive/(:year/:month/:day|/:month/:year)"
Wie wäre es dann mit einer eigenen Syntax?

Code: Alles auswählen

"/archive/(:year/:month/:day|/:month/:year){int}"

"/move/:id{int}/:x{float}/:y{float}"
Will man nicht auf Typen prüfen, lässt man es bei dem entsprechenden Argument weg (also keine Klammern).

Keine Ahnung, inwiefern sich das im regelmäßigen Gebrauch als praktikabel erweisen würde und man muss natürlich aufpassen, dass sich Regex-Syntax und eigene Syntax nicht beißen.

Verfasst: Sonntag 5. Juli 2009, 08:20
von sma
Vieleicht ist es doch Zeit für Python 3.x:

Code: Alles auswählen

#!/usr/bin/env python3.1

def typecheck(f):
    def wrapper(*args):
        args = [f.__annotations__[n](a)
            if n in f.__annotations__ else a
            for a, n in zip(args, f.__code__.co_varnames)]
        return f(*args)
    return wrapper

@typecheck
def test(a: int):
    print(a)

test(42)
test("Hallo, Welt")
Stefan

Verfasst: Sonntag 5. Juli 2009, 12:28
von lunar
Man kann sich das Routing auch von Werkzeug abschauen ... dessen Routing-Syntax erlaubt Typangaben in Regeln, ist aber trotzdem nicht komplex.

Die Validierung vom Routing zu trennen, mag zwar konzeptionell elegant sein, hat aber praktische Nachteile. Man kann beispielsweise nicht nach Typen routen. Außerdem zwingt es zu imho unnötigen Duplizierung von Informationen. Die Argumente eines Views müssen so an drei verschiedenen Stellen angegeben werden: In der Funktionssignatur, in der Route und im Validierungsdekorator.

Verfasst: Montag 6. Juli 2009, 17:39
von Defnull
lunar hat geschrieben:Man kann sich das Routing auch von Werkzeug abschauen ... dessen Routing-Syntax erlaubt Typangaben in Regeln, ist aber trotzdem nicht komplex.
Die Routing-Komponente von Werkzeug ist aber auch 2 mal so groß wie mein ganzes Framework ;)

Ich hab länger drüber nach gedacht und werde den Routing-Syntax voraussichtlich nicht um Typen erweitern. Aus folgenden Gründen:

Aus Prinzip: Routen routen. Das ist ihre Aufgabe. Was nicht zu ihren Aufgaben gehört ist das Verändern und Prüfen von Objekten. In URLs gibt es nämlich keine Objekte, sondern lediglich Strings. Für mich gehört das Validieren und Umwandeln von Parametern nicht in die Routen, sondern in dien Logik-Code. Da, wo man auch sonst die Verarbeitung von Parametern erwarten würde.

Aus technischen Gründen: Momentan basiert das komplette Routing auf re.match() und match.groupdict(). Beide sind sehr schnell und unkompliziert, unterstützen aber nativ keine Typen-Umwandlung. Ein eigener Syntax für Typen würde den Routing-Teil des Frameworks daher um nicht wenige Zeilen Code erweitern, ohne einen wirklichen Vorteil zu bringen.

Weil es überflüssig wäre: Alles, was man mit dem erweiterten Syntax erreichen könnte, ist jetzt schon mit dem validiate() dekorator möglich. Dieser ist bereits enthaltenen und löst das Problem eigentlich sehr einfach und übersichtlich. Außerdem erlaubt er sogar noch das Prüfen und Verändern komplexer Parameter durch lambda oder eigene callbacks. Lambda in den Routing-Syntax einzubauen wäre sicher nicht mehr ganz so einfach.


Aber ich mach folgende Kompromisse:
Man wird dem validator sagen können, das er, statt einen 400er zu werfen, einfach die entsprechende Route ignorieren soll. Außerdem wird der route() dekorator um as validate-Argument erweitert, wenn man zu faul ist, die dekoratoren selbst aneinander zu ketten.

Beides in der nächsten Version (voraussichtlich noch heute)

Re: Bottle: Micro Web Framework

Verfasst: Montag 6. Juli 2009, 20:27
von kbr
Defnull hat geschrieben:Nach etwa 4 Tagen und unzähligen Tassen Kaffee kann ich euch endlich mein eigenes Micro Web Framework präsentieren :D
Sehr schön - ein eigenes kleines Framework schreiben macht Spaß. Kennst Du bobo schon? Jim Fulton hat es noch einmal neu aufgelegt :)

Verfasst: Montag 6. Juli 2009, 20:38
von Defnull
OK, der Validator muss warten, heute hab ich erst einmal Support für sehr einfache Templates hinzu gefügt (+150 Zeilen, langsam wird mein 'micro' Framework etwas groß...)

Einen extra-thread dazu gibts hier

Beispiel:

Code: Alles auswählen

@route('/test')
def template_test():
    items = ['Bottle is nice!', 2, 0.09]
    render('example', items=items)

Code: Alles auswählen

%header = 'Test Template'
<html>
  <head>
    <title>{{header.title()}}</title>
  </head>
  <body>
    <h1>{{header.title()}}</h1>
    <p>Items in list: {{len(items)}}</p>
    <ul>
    %for item in items:
      <li>
      %if isinstance(item, int):
        Zahl: {{item}}
      %else:
        %try:
        Other type: ({{type(item).__name__}}) {{repr(item)}}
        %except:
        Error: Item has no string representation.
        %end try-block (yes, you may add comments here)
      %end
      </li>
    %end
    </ul>
  </body>
</html>
Der Template-Syntax ist extrem simpel:
1) Ein '%' markiert eine Zeile mit Python-Code. Um die richtige Einrückung muss man sich nicht kümmern. Die wird vom Template Parser automatisch berechnet.
2) Ein '% end' beendet einen Python Block. Das ist notwendig, damit der Parser die Blöcke richtig erkennt.
3) '{{...}}' wird im Text durch den darin enthaltenen Python-Ausdruck ersetzt.

Diese Template-Engine ist zwar recht simpel, kann aber praktisch alles, was Python auch kann und ist extrem schnell dabei. Das Beispiel rendert mit der Bottle-eigenen Engine zwischen 7 und 10 mal schneller als ein vergleichbares Template mit Mako, obwohl Mako von sich selbst behauptet, 'Insanely Fast' zu sein ;)

In den nächsten paar Minuten wird trotzdem ein Wrapper für Mako-Templates dazu kommen. Bottle möchte sich ja nicht aufdrängen, sondern nur gute Alternativen bieten, falls keine externe Template-Engine zur Verfügung steht.


Edit: @kbr Auf den ersten Blick sieht mir bobo etwas zu minimal aus. Da fehlt einiges an Features, bis es (für mich) wirklich nützlich wird.

Verfasst: Dienstag 7. Juli 2009, 00:47
von Defnull

Verfasst: Donnerstag 9. Juli 2009, 08:33
von snafu
Planst du eigentlich auch eine kleine Datenbankanbindung für die Zukunft? :)

Verfasst: Donnerstag 9. Juli 2009, 09:25
von Defnull
Eigentlich nicht. Es gibt da sehr gute Angebote (SQLAlchemy, Elixir, ...) und jede hat seine Vor- und Nachteile. Man kann da nie das Richtige machen, da eh jeder seine eigenen Vorstellungen und Vorlieben hat. Außerdem hab ich nur noch 250 Zeilen Platz und ORMs sind extrem komplex. Das wird nix.

Ich werde aber wohl noch eine kleine Key-Value Datenbank einbauen, die man für Sessions, Multi-Process-Persistenz und Caching nutzen kann. Mit anydbm als Persistenz-Layer und Memcache (wenn vorhanden) als cache. Das ist nämlich sehr nützlich und gar nicht mal so trivial für den Anwender selbst zu basteln (trigger db.close() on request exit; create new dbs on demand, open dbs on demand, ...)

Verfasst: Donnerstag 9. Juli 2009, 13:28
von Defnull
So, fertig. Bottle bietet nun persistente key/value Datenbanken an (siehe doku). Die Version 0.4.6 ist schon im git Repository, aber noch nicht im pypi, da ich noch auf die Antwort von SMA tum Thema Templates warte.

Verfasst: Donnerstag 9. Juli 2009, 14:51
von /me
Defnull hat geschrieben:So, fertig. Bottle bietet nun persistente key/value Datenbanken an (siehe doku).
Es ist klein, schön und handlich. Achte bitte darauf, dass du nicht anfängst das Projekt zu einem Monster aufzublasen. ;-)

Gruß,
Matthias

Verfasst: Donnerstag 9. Juli 2009, 15:12
von Defnull
Ich bleibe unter 1000 Zeilen :) Und mir fällt auch gerade kein fehlendes Feature mehr ein. Nun wird nur noch optimiert ;)

Verfasst: Donnerstag 9. Juli 2009, 18:11
von snafu
Vielleicht mache ich ja etwas falsch, aber bei mir führt `from bottle import db` zu einem ImportError. Installiert habe ich deine aktuellste Version mittels `python setup.py install`. Ich würde die Datenbankfähigkeit sehr gerne ausprobieren, da ich noch ganz neu im Bereich der Webframeworks bin und mich die Einfachheit von Bottle ziemlich überzeugt. Werden die Einträge in der Datenbank eigentlich dauerhaft in eine Datei gespeichert oder gilt das immer nur für eine Serversitzung?

Verfasst: Donnerstag 9. Juli 2009, 18:52
von Dauerbaustelle
Das ist garkein Extra-Modul, das DB-Zeug. Jedenfalls gerade nicht.

Verfasst: Donnerstag 9. Juli 2009, 19:04
von Defnull
snafu hat geschrieben:Vielleicht mache ich ja etwas falsch, aber bei mir führt `from bottle import db` zu einem ImportError. Installiert habe ich deine aktuellste Version mittels `python setup.py install`. Ich würde die Datenbankfähigkeit sehr gerne ausprobieren, da ich noch ganz neu im Bereich der Webframeworks bin und mich die Einfachheit von Bottle ziemlich überzeugt. Werden die Einträge in der Datenbank eigentlich dauerhaft in eine Datei gespeichert oder gilt das immer nur für eine Serversitzung?
Hast du Version 0.4.6? (erst heute nachmittag erschienen)
Mach mal ein: easy_install -U bottle
Dann sollte das gehen.

Code: Alles auswählen

>>> from bottle import db
>>> db
<bottle.BottleDB object at 0x95baf2c>
>>> db.newdb.key1 = "value1"
>>> db.newdb.key1
'value1'
>>> db.save()
Wobei ich grad nen Fehler gefunden habe und 0.4.7 veröffentliche, Moment bitte.

Edit: Done. Der DB-Kram ist aber noch Verbesserungswürdig. Ich lasse BottleBucket z.B. von dict erben, implementiere aber nicht alle Methoden. Das ist alles noch etwas unsauber, aber es funktioniert.

Verfasst: Freitag 10. Juli 2009, 05:23
von snafu
Über easy_install hatte ich noch 0.4.3 drauf, jetzt - laut der Meldung - 0.4.6. Import geht auf jeden Fall, Details probiere ich später aus. :)

Verfasst: Montag 13. Juli 2009, 18:13
von Defnull
Es gibt noch genau ein Feature, das ich früher oder später noch umsetzen möchte: Umgekehrte routen (Generierung von URLs aus parametrisierten benannten Routen und passenden Parametern).

Ansonsten ist Bottle (was die Features an geht) fertig!

In den folgenden Tagen werde ich noch ein paar TestCases schreiben und auf Käferjagd gehen. Dann wird nur noch optimiert und 2to3 kompatibel gemacht. Eventuell lasse ich mich davon überzeugen, noch ein paar zusätzliche WSGI-Gateways oder Template Engines mit Adaptern zu versehen, aber eigentlich ist alles wichtige drin.

Damit wandle ich diesen Thread nun offiziell in einen Bottle Support und Feedback Thread um :)

Zum Abschluss noch ein paar Links:

GIT Repository
Dokumentation und WIKI
PyPi Eintrag

Verfasst: Montag 13. Juli 2009, 18:47
von cryzed
Hallo Defnull. Ich bin relativ neu und unerfahren was Webframeworks und Web-Programming mit Python angeht. Ehrlich gesagt mit Server-Geschichten allgemein.

Ich habe mir neulich einen vServer gemietet und dort MySQL sowie Apache2 richtig eingerichtet und konfiguriert und hoste jetzt unter cryzed.de meinen Wordpress Blog. Mit einem Virtualhost in der Apache2 config habe ich mir außerdem eine subdomain nach /var/www/minecraft/ geschaltet, eine kleine Wiki für ein Indie Spiel welches ich Spiele und auch einen Server dafür betreibe.

Gibt es eine Möglichtkeit Bottle, oder viel eher Apache2 zu sagen das wenn ich die subdomain minecraft.cryzed.de aufrufe er automatisch den port 8080 (oder einen anders definierten) Port ansprechen soll, so das er auf dem eigenen Webserver von Bottle landet? Ich wollte nämlich nicht gleich den Apache2 und somit auch den MySQL Server aufgeben - ergo meinen Block nur weil ich ein kleines Projekt mit Bottle schreiben/austesten wollte. Ich wäre sehr froh über eine Antwort.

PS: Es wäre klasse falls das mit den Virtualhosts geht wenn mir jemand eine leicht angepasste default-config hier posten könnte, wie gesagt ich bin leider noch ziemlich neu auf den Gebiet habe aber ehrlich gesagt keine Lust mich jetzt 1-2 Stunden mit Apache2 zu beschäftigen bevor ich meine ersten Schritte mit Bottle machen kann, obwohl das wahrscheinlich die intelligentere Lösung wäre - aber wie man sieht gehts auch (fast) ohne lesen - sonst ständ mein Blog nicht :P.

Verfasst: Montag 13. Juli 2009, 18:57
von Defnull
Ich nutze selbst lighttpd und kein Apache, aber Onkel Google spuckt mir folgendes aus:

Code: Alles auswählen

<VirtualHost *>
    ServerName python.example.com

    ProxyPass / http://localhost:8080/ retry=5
    ProxyPassReverse / http://localhost:8080/
    ProxyPreserveHost On
    <Proxy *>
        Order deny,allow
        Allow from all
    </Proxy>
</VirtualHost>