 |
Das deutsche Python-Forum Diskussionen rund um die Programmiersprache Python
|
| Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
| Autor |
Nachricht |
Defnull User

Anmeldungsdatum: 18.06.2009 Beiträge: 539 Wohnort: Göttingen
|
Verfasst am: Do Jul 02, 2009 17:39 Titel: Bottle: Micro Web Framework |
|
|
Nach etwa 4 Tagen und unzähligen Tassen Kaffee kann ich euch endlich mein eigenes Micro Web Framework präsentieren
bottle.py
( Der Name hat einen Sinn! Ich muss nur noch herausfinden, welchen )
Ich werde es in Zukunft für meine eigenen Web-Projekte verwenden und auf diese Weise ausgiebig testen und weiter verbessern. Das nächste Update wird also nicht lange auf sich warten lassen. Das ganze steht unter einer MIT Lizenz und ist demnach ziemlich offen. Über Feedback, Betatester und Forks würde ich mich freuen
Und wer zu faul ist, auf den Link da oben zu klicken, kann sich eine kleine Beispiel-Applikation auch gleich hier ansehen:
| Code: (Python) | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| from bottle import route, run, request, response, send_file, abort
@route('/')
def hello_world():
return 'Hello World!'
@route('/hello/:name')
def hello_name(name):
return 'Hello %s!' % name
@route('/hello', method='POST')
def hello_post():
name = request.POST['name']
return 'Hello %s!' % name
@route('/static/:filename#.*#')
def static_file(filename):
send_file(filename, root='/path/to/static/files/')
@route('/counter')
def counter():
old = request.COOKIE.get('counter',0)
new = int(old) + 1
response.COOKIE['counter'] = new
return "You viewed this page %d times!" % new
@route('/private')
def private():
if request.POST.get('password','') != 'secret':
abort(401, 'Go away!')
return "Welcome!"
run(host='localhost', port=80)
|
PS: Doppelpost zu diesem Beitrag. Sorry dafür, aber ich denke das passt besser in einen neuen thread. |
|
| Nach oben |
|
 |
stuhlbein User
Anmeldungsdatum: 09.01.2009 Beiträge: 88
|
Verfasst am: Fr Jul 03, 2009 00:02 Titel: |
|
|
gefällt mir gut das projekt!
da ich bei dem thema WSGI ebenfalls neu bin, werd ich mir das die tage auf jedenfall mal genauer anschauen, das gebrachte beispiel sieht jedenfalls gut aus  |
|
| Nach oben |
|
 |
audax User
Anmeldungsdatum: 19.12.2007 Beiträge: 828
|
Verfasst am: Fr Jul 03, 2009 06:07 Titel: |
|
|
Mag es sein, dass das Ding nicht Threadsafe ist?
Kommt mir so vor, weil der Request nicht direkt den Funktionen übergeben wird und dadurich ne Race-Condition entsteht. |
|
| Nach oben |
|
 |
snafu User

Anmeldungsdatum: 21.02.2008 Beiträge: 1369 Wohnort: Gelsenkirchen
|
Verfasst am: Fr Jul 03, 2009 07:04 Titel: |
|
|
Ich glaube, es wäre einfacher, wenn man das OPTIMIZER-Flag per Keyword-Argument ändern könnte anstatt es im Quelltext umstellen zu müssen.
Und außerdem sollte es bei den Errorseiten IMHO möglich sein, den vorgegebenen HTML-Code bei Bedarf ändern zu können.
Aber an sich finde ich die API schon ziemlich cool. Leider nur 2 Buchstaben zu viel, um als echtes Microframework gelten zu können. ;P _________________ http://lord-killux.mybrute.com |
|
| Nach oben |
|
 |
Defnull User

Anmeldungsdatum: 18.06.2009 Beiträge: 539 Wohnort: Göttingen
|
Verfasst am: Fr Jul 03, 2009 08:31 Titel: |
|
|
@Bitfish: Danke Bei Fragen, frag.
@audax: Die Modul-Variablen request und response sind beides Instanzen von Request bzw. Response die wiederum von threading.local erben und bei jeder Anfrage mit den gerade aktuellen Daten gefüttert werden. Das hat sich als performanter und auch angenehmer erwiesen, als die Objekte bei jeder Anfrage frisch zu erschaffen und an die Handler zu übergeben. Ist also alles Thread-Save
| snafu hat folgendes geschrieben: | | Ich glaube, es wäre einfacher, wenn man das OPTIMIZER-Flag per Keyword-Argument ändern könnte anstatt es im Quelltext umstellen zu müssen. |
Jup, wäre es. Ist in der aktuellen Version (0.3.3) geändert. Außerdem ist es nun per default aus geschaltet, da das umsortieren von Routen zu komischen Ergebnissen führen kann, wenn sich Routen überschneiden (/hello/world und /hello/:name) und der Benutzer nichts vom Optimierer weis.
| snafu hat folgendes geschrieben: | | Und außerdem sollte es bei den Errorseiten IMHO möglich sein, den vorgegebenen HTML-Code bei Bedarf ändern zu können. |
Du hast mit eigenen error-handlern volle Kontrolle über den output, also das, was an den Browser zurück gesendet wird. Alle Nicht-HTTP-Exceptions resultieren in einem 500er Fehler. Wenn man sein eigenes Debugging-Framework an die 500 bindet, kann man eigentlich alles machen, was man will
| snafu hat folgendes geschrieben: |
Aber an sich finde ich die API schon ziemlich cool. Leider nur 2 Buchstaben zu viel, um als echtes Microframework gelten zu können. ;P |
botl.py  _________________ Bottle: Micro Web Framework |
|
| Nach oben |
|
 |
snafu User

Anmeldungsdatum: 21.02.2008 Beiträge: 1369 Wohnort: Gelsenkirchen
|
|
| Nach oben |
|
 |
Defnull User

Anmeldungsdatum: 18.06.2009 Beiträge: 539 Wohnort: Göttingen
|
Verfasst am: Fr Jul 03, 2009 10:06 Titel: |
|
|
Buffered Transistor Transistor Logic?
Version 0.3.4 enthält nun auch ein etwas umfassenderes Beispiel
Version 0.3.5 behebt einen Fehler im POST Daten Parser und verfügt über etwas aussagekräftigere Fehlermeldungen im DEBUG Modus. _________________ Bottle: Micro Web Framework |
|
| Nach oben |
|
 |
sma User
Anmeldungsdatum: 19.11.2007 Beiträge: 2132 Wohnort: Kiel
|
Verfasst am: Sa Jul 04, 2009 08:42 Titel: |
|
|
Nicht schlecht. Wäre dein Ziel Minimalismus, würde ich die ##-Syntax bei @route weglassen. Ich sehe nicht, warum ich noch eine Alternative zu regulären Ausdrücken brauche. Den Namen muss man sowieso als Funktionsparameter in der nächste Zeile wiederholen. Nett fände ich einen Weg, bei einem Parameter gleich anzugeben, ob das z.B. ein int sein muss. Andernfalls soll das Rahmenwerk gleich ein 400 werfen. Allgemein fände ich es hilfreich, wenn Parameter von Formularen (ähnlich wie es Rails automatisch macht) vorverarbeitet werden. Den threadlokalen Request finde ich gut. Das hält die Funktionsparameterliste übersichtlich. Das Tauschen von Matchern in der Liste der URLs mit einer Wahrscheinlichkeit von 1:1000 finde ich nach wie vor komisch.
Hast du dir auch eine Anbindung an Template-Engines überlegt oder willst du eine Micro-Templating-Engine integrieren? |
|
| Nach oben |
|
 |
Defnull User

Anmeldungsdatum: 18.06.2009 Beiträge: 539 Wohnort: Göttingen
|
Verfasst am: Sa Jul 04, 2009 14:59 Titel: |
|
|
| sma hat folgendes geschrieben: | | Wäre dein Ziel Minimalismus, würde ich die ##-Syntax bei @route weglassen. |
Im Quelltext von Bottle ist das eine einzige Zeile und ich persönlich nutze es eigentlich recht gerne. Ich mag den "(?P<name>...)" Symtax nicht und nicht jeder kennt ihn überhaupt. Optional ist er natürlich trotzdem erlaubt.
| sma hat folgendes geschrieben: | | Nett fände ich einen Weg, bei einem Parameter gleich anzugeben, ob das z.B. ein int sein muss. Andernfalls soll das Rahmenwerk gleich ein 400 werfen. |
Das ist einfacher mit einem neuen Dekorator zu lösen, aber ne gute Idee.
| sma hat folgendes geschrieben: | | Allgemein fände ich es hilfreich, wenn Parameter von Formularen (ähnlich wie es Rails automatisch macht) vorverarbeitet werden. |
Autocast zu int oder float für alles, was wie ne Zahl aus sieht, mache ich absichtlich nicht. Das gehört in die Kategorie 'magic features' die eher nicht in so ein Framework gehören. Man muss eh prüfen, ob man vom Framework nen int oder nen string bekommt. Dann kann man auch gleich das Casting selbst machen.
| sma hat folgendes geschrieben: | | Den threadlokalen Request finde ich gut. Das hält die Funktionsparameterliste übersichtlich. |
Das war der Hauptgrund für diese Entscheidung
| sma hat folgendes geschrieben: | | Das Tauschen von Matchern in der Liste der URLs mit einer Wahrscheinlichkeit von 1:1000 finde ich nach wie vor komisch. |
Und per default aus geschaltet. Ich hab aber Tests gemacht: Schon bei 20 Routen macht die Position der Route in der Prüfliste einen Unterschied von bis zu 100 Requests/Sekunde (bei 400-500#/s im Durchschnitt, also 20-25%). Schließlich ist das Routing mit das rechenintensivste, das überhaupt im Framework passiert.
| sma hat folgendes geschrieben: | | Hast du dir auch eine Anbindung an Template-Engines überlegt oder willst du eine Micro-Templating-Engine integrieren? |
Ich wollte genau wie bei den WSGIServer auch für die verschiedenen Template Engines einheitliche Adapter basteln, damit man sie jederzeit nach belieben austauschen kann, ohne den App-Code zu ändern. Der erste Adapter wird wohl mako, weil ich das selbst sehr gerne verwende. _________________ Bottle: Micro Web Framework |
|
| Nach oben |
|
 |
Dauerbaustelle User
Anmeldungsdatum: 09.01.2008 Beiträge: 547
|
Verfasst am: Sa Jul 04, 2009 15:07 Titel: |
|
|
| Defnull hat folgendes geschrieben: |
| sma hat folgendes geschrieben: | | Nett fände ich einen Weg, bei einem Parameter gleich anzugeben, ob das z.B. ein int sein muss. Andernfalls soll das Rahmenwerk gleich ein 400 werfen. |
Das ist einfacher mit einem neuen Dekorator zu lösen |
Nein, wie soll denn der Dekorator aussehen? Bedenke, dass du innerhalb einer URL mehrere verschiedene Typen haben möchtest. _________________ github | bitbucket |
|
| Nach oben |
|
 |
Defnull User

Anmeldungsdatum: 18.06.2009 Beiträge: 539 Wohnort: Göttingen
|
Verfasst am: Sa Jul 04, 2009 17:11 Titel: |
|
|
| Dauerbaustelle hat folgendes geschrieben: | | Defnull hat folgendes geschrieben: |
| sma hat folgendes geschrieben: | | Nett fände ich einen Weg, bei einem Parameter gleich anzugeben, ob das z.B. ein int sein muss. Andernfalls soll das Rahmenwerk gleich ein 400 werfen. |
Das ist einfacher mit einem neuen Dekorator zu lösen |
Nein, wie soll denn der Dekorator aussehen? Bedenke, dass du innerhalb einer URL mehrere verschiedene Typen haben möchtest. |
Ungetestetes Beispiel
| Code: (Python) | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
def validate(**vkargs):
def decorator(func):
def wrapper(**kargs):
for key in vkargs:
if key not in kargs:
abort(400, 'Missing parameter: %s' % key)
try:
kargs[key] = vkargs[key](kargs[key])
except ValueError, e:
abort(400, 'Wrong parameter format for: %s' % key)
return func(**kargs)
return wrapper
return decorator
@route('/move/:id/:x/:y')
@validate(id=int, x=float, y=float)
def move(id, x, y):
pass
|
Erklärung: Man übergibt validate() für jeden Parameter, den man testen möchte, eine Callable. Diese sollte die Eingabe wie gewünscht umwandeln und zurück geben oder ValueError werfen, wenn die Umwandlung nicht möglich ist. Alternativ kann man im Callable auch gleich HTTPError werfen oder (besser) abort() benutzen um dem User ne individuelle Fehlermeldung um die Ohren zu hauen
Konvertierung in alle Python Standard-Typen (int, float, ...) sind so direkt möglich. Mit eigenen Callables kann man aber noch viel mehr tun, zum Beispiel vollwertige Form-Validation. Für def-faule gibts ja auch noch lambda
| Code: (Python) | 1 2 3 4 5 6 7
|
@route('/add_list/:csv')
@validate(cvs=lambda x: map(int, x.strip().split(',')))
def add_list(cvs):
''' Adds a list of IDs (separated by ',') '''
pass
|
Hmm ich glaub den Dekorator bau ich genau so ein  _________________ Bottle: Micro Web Framework
Zuletzt bearbeitet von Defnull am Sa Jul 04, 2009 17:24, insgesamt einmal bearbeitet |
|
| Nach oben |
|
 |
Dauerbaustelle User
Anmeldungsdatum: 09.01.2008 Beiträge: 547
|
Verfasst am: Sa Jul 04, 2009 17:16 Titel: |
|
|
Warum ein Extra-Validation-Element, wenn man die Validation auch gleich in die URL-Parameter reinmachen könnte? _________________ github | bitbucket |
|
| Nach oben |
|
 |
Defnull User

Anmeldungsdatum: 18.06.2009 Beiträge: 539 Wohnort: Göttingen
|
Verfasst am: Sa Jul 04, 2009 17:27 Titel: |
|
|
| Dauerbaustelle hat folgendes geschrieben: | | Warum ein Extra-Validation-Element, wenn man die Validation auch gleich in die URL-Parameter reinmachen könnte? |
Weil das den URL-Syntax zu kompliziert macht und mit nem Dekorator um einiges übersichtlicher ist. (Meine Meinung) Außerdem ist es (rein prinzipiell) nicht Aufgabe der Routen, die Parameter zu verändern.
Edit: Version 0.3.7 enthält den neuen decorator  _________________ Bottle: Micro Web Framework |
|
| Nach oben |
|
 |
snafu User

Anmeldungsdatum: 21.02.2008 Beiträge: 1369 Wohnort: Gelsenkirchen
|
Verfasst am: So Jul 05, 2009 06:37 Titel: |
|
|
| Defnull hat folgendes geschrieben: | | sma hat folgendes geschrieben: | | Wäre dein Ziel Minimalismus, würde ich die ##-Syntax bei @route weglassen. |
Im Quelltext von Bottle ist das eine einzige Zeile und ich persönlich nutze es eigentlich recht gerne. Ich mag den "(?P<name>...)" Symtax nicht und nicht jeder kennt ihn überhaupt. Optional ist er natürlich trotzdem erlaubt. |
Der Meinung bin ich übrigens auch. Gerade dieser Punkt ist mir sehr positiv ins Auge gesprungen. Es gibt bestimmt einige Leute (ich bin einer davon), die soetwas tausendmal lieber benutzen als Regexes.
| Defnull hat folgendes geschrieben: | | Dauerbaustelle hat folgendes geschrieben: | | Warum ein Extra-Validation-Element, wenn man die Validation auch gleich in die URL-Parameter reinmachen könnte? |
Weil das den URL-Syntax zu kompliziert macht und mit nem Dekorator um einiges übersichtlicher ist. |
Man könnte zusätzlich zum Dekorator noch `validate` als Keywordargument anbieten, welches ein Dictionary erwartet:
| Code: (Python) | 1 2 3
| @route('/move/:id/:x/:y', validate=dict(id=int, x=float, y=float))
def move(id, x, y):
pass |
EDIT: Oder die Typen in einer Liste übergeben, ähnlich wie ctypes das bei den Argumenten macht. Ist vielleicht noch etwas besser. _________________ http://lord-killux.mybrute.com
Zuletzt bearbeitet von snafu am So Jul 05, 2009 06:54, insgesamt 2-mal bearbeitet |
|
| Nach oben |
|
 |
SchneiderWeisse User
Anmeldungsdatum: 06.11.2007 Beiträge: 740
|
Verfasst am: So Jul 05, 2009 06:45 Titel: |
|
|
| das mit dem validate Dekorator gefällt mir irgendwie nicht... finde es besser und einfacher direkt in der URL-Rule. |
|
| Nach oben |
|
 |
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
Powered by phpBB © 2001, 2005 phpBB Group using CodeBB 1.1
|