Seite 4 von 30
Verfasst: Freitag 17. Juli 2009, 22:09
von Dauerbaustelle
Defnull hat geschrieben:Dass ValueErrors abgefangen und als Indikator für "In der URL steht Mist" genutzt werden können, wäre allerdings einen Eintrag in der Doku wert, da hast du recht.
Jop, ich finde nämlich, dass das ein wenig verwirrt - die Möglichkeit der Verwendung von Funktionen als Validatoren reicht so nach "ich hab alle Freiheit", aber die hab ich ja nicht.
Ansonsten: Super Framework, tausend mal schneller als zB "itty" oder sowas. Die Datenbank- und Templateanbindung solltest du meiner Meinung nach auslagern (weil ich das nicht benötige), aber das ist Ansichtssache.
Achja, es sollte noch die Möglichkeit geben, für eine URL GET und POST zu erlauben.
Gruß
Verfasst: Freitag 17. Juli 2009, 22:39
von Defnull
Danke für das Lob
Dauerbaustelle hat geschrieben:Achja, es sollte noch die Möglichkeit geben, für eine URL GET und POST zu erlauben.
Einfach zwei Routen registrieren
Code: Alles auswählen
@route('/hello')
@route('/hello', method='POST')
def hello():
pass
Verfasst: Samstag 18. Juli 2009, 17:46
von Defnull
Neue Version (0.4.14) mit einigen bugfixes. Ich empfehle jedem, der bottle bereits nutzt, ein "easy_install -U bottle"
Verfasst: Montag 20. Juli 2009, 07:28
von noisefloor
Hallo,
wie kann ich denn bei bottle feststellen, in welcher Route ich mich gerade befinde?
Gruß, noisefloor
Verfasst: Montag 20. Juli 2009, 10:42
von Dauerbaustelle
Naja, eigentlich solltest du das "wissen", weil du eine Route ja an eine Funktion bindest.
Verfasst: Montag 20. Juli 2009, 11:11
von Defnull
Zielfunktion und URL-Parameter kennst du ja bereits, wenn du dich schon im Request Handler befindest. Dich könnte also höchstens der reguläre Ausdruck interessieren, der zum Treffer geführt hat:
Ungetesteter Vorschlag:
Code: Alles auswählen
@bottle.route(...)
def test(**kargs):
url = bottle.request.path.strip().lstrip("/ ")
method = bottle.request.method
# Statische routen werden immer zuerst gesucht:
handler = bottle.ROUTES_SIMPLE.get(method,{}).get(url, None)
if handler:
return "Statische Route: %s" % url
# Komplexe Routen sind etwas anders gespeichert
for regexp, handler in bottle.ROUTES_REGEXP.get(method,[]):
if regexp.match(url):
return "Komplexe Route: %s" % regexp.pattern
Verfasst: Montag 20. Juli 2009, 13:45
von noisefloor
Hallo,
Danke, wobei ich einen Denkfehler hatte, als ich im Kopf gecoded habe
BTW, bottle ist das 1. Framework, mit dem ich WSGI verstanden habe / umsetzen werde. Werde es demnächst mal einsetzen.
Mehr Fragen kommen dann noch.
Gruß, noisefloor
Verfasst: Mittwoch 22. Juli 2009, 14:21
von Defnull
Verfasst: Samstag 25. Juli 2009, 18:57
von Defnull
Bottle 0.5.3 ist draußen
Was hat sich geändert?
- Nativer Support für Python 2.6+ und 3.x (dafür kein Support mehr für Python 2.5 und älter. Sorry)
- Besseres Handling von Multipart Data.
- Besserer Support für Middleware und multi-application Umgebungen.
- Viele neue UnitTests.
Also hauptsächlich Dinge unter der Haube. Der große Versionssprung kam durch die Inkompatibilität mit Python 2.5. Bottle 0.4.x ist und bleibt aber Python2.5 kompatibel.
Verfasst: Samstag 25. Juli 2009, 21:33
von noisefloor
Hallo,
wenn ich ein Applikation unter Bottle Stand-Alone laufen lassen findet Bottle alle meine Templates, wenn ich es per WSGI mache (wie von jerch oben beschrieben) läuft auch alles, AUSSER das Bottle keine Templates findet.
Hat jemand eine Idee, woran das liegt? Mussen die Templates einem bestimmen Nutzer gehören (z.B. www-data)?
Gruß, noisefloor
Verfasst: Samstag 25. Juli 2009, 22:52
von Defnull
Per Default sucht Bottle die Templates in bottle.TEMPLATE_PATH = ['./%s.tpl', './views/%s.tpl']
Das ist noch nicht so ideal, da './' in machen Umgebungen eben nicht im Projektverzeichnis liegt.
Ein Workaround wäre: bottle.TEMPLATE_PATH.insert(0,'/absoluter/pfad/zu/den/Templates/%s.tpl') aber ich werde mir da noch was besseres überlegen müssen.
In der neuesten Version wurde WSGIHandler übrigens in Bottle umbenannt. Um den richtigen WSGI-Handler zu bekommen, gibts dafür bottle.default_app().
Verfasst: Samstag 25. Juli 2009, 23:13
von Dauerbaustelle
Achja, nochwas, was mich stört: Wenn ich dem Validator was mitgegeben habe, für eine Funktion aber eine Route ohne (diesen) Parameter habe, krieg ich trotzdem nen Error;
Code: Alles auswählen
In [1]: import bottle
In [2]: from bottle import route, validate
In [3]: @route('/foo/:bar/')
...: @route('/foo/')
...: @validate(bar=int)
...: def blubb(bar=None): print(bar)
...:
In [4]: handle = lambda func, args: func(**args)
In [5]: handle(*bottle.match_url('/foo/42/'))
42
In [6]: handle(*bottle.match_url('/foo/'))
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
...
HTTPError: Missing parameter: bar
Das Problem ist hier IMHO dass bottle sofort abbricht und nicht nach weiteren Routen sucht, die eventuell matchen würden. Oder, dass der Validator Werte erzwingt.
Verfasst: Samstag 25. Juli 2009, 23:29
von Dauerbaustelle
Achja und nochwas, wenn ich jetzt meine eigene von `Bottle` erbende Subklasse schreibe, wie route ich dann mit der? Muss ich die Routing-Funktion dafür dann selbst wrappen?!
Verfasst: Samstag 25. Juli 2009, 23:45
von Defnull
Eine Exception, die Routen überspringt, steht schon seit 0.4.8 auf meiner ToDo Liste >.< Schande über mich! Damit wär das gewünschte Verhalten problemlos in validate() erreichbar...
Würde es Sinn machen, das validate alternativ als route()-Parameter zu lösen?
Mit dem subclassen von Bottle würde ich noch etwas warten, daran bastel ich gerade recht aktiv rum. Die API wird sich also noch ändern.
Momentan gibt bottle.default_app() ein Singleton der Bottle() Klasse zurück. Das halbe Framework arbeitet damit. Wenn du Bottle subclassen willst, kannst du daher die Modul-Level-Dekoratoren nicht mehr verwenden. Die sehen nämlich so aus:
Code: Alles auswählen
def route(url, **kargs):
return default_app().route(url, **kargs)
Macht aber nix, @yoursubclassobject.route('...') würde nämlich funktionieren.
Auch run() arbeitet mit default_app(), es sei denn du übergipst deine Applikation als 'app' Parameter. Mal ein Beispiel:
Code: Alles auswählen
import bottle
class MyBottle(bottle.Bottle):
...
myapp = MyBottle()
@myapp.route('/hello'):
def handler():
return 'Hello MyWorld!'
bottle.run(app=myapp)
Ich frag mich allerdings, wozu du Bottle subclassen willst? Middleware wäre einfacher mit folgendem Code:
Code: Alles auswählen
import bottle
... normaler bottle krams...
bottle.run(app=MiddleWare(app=bottle.default_app()))
Dafür überleg ich mir aber eh noch was besseres

Support für Middleware war der Hauptgrund für die ganze Umstrukturiererei im letzten Update.
Verfasst: Sonntag 26. Juli 2009, 00:23
von Dauerbaustelle
Gibt es denn schon Middleware-Unterstützung? Naja, die Subklasse habe ich gemacht, damit der URL-Matcher keinen Unterschied zwische /foo/bar und /foo/bar/ macht, weil mich das sehr gestört hat. Im Prinzip musste ich nur route wrappen, den Rest brauche ich garnicht (abort usw braucht ja keine app ;)).
Gruß
Verfasst: Sonntag 26. Juli 2009, 00:34
von Defnull
Das mit "/bla" != "/bla/" war eine recht frühe Design-Entscheidung. Ich finde die Unterscheidung schlicht sauberer, schließlich sind es zwei verschiedene Pfade. Zur Not kann man immer noch @route('/bla/?') benutzen
Ich würde aber eher match_url wrappen:
Code: Alles auswählen
class MyBoddle(Bottle):
def match_url(self, url, *args, **kargs):
return Bottle.match_url(self, url.rstrip('/'), *args, **kargs)
default_app(MyBoddle())
Mit der aktuellen GitHub Version kannst du übrigens default_app(myapp) verwenden, um deine eigene Instanz von Bottle() als default zu registrieren. Dann kannst du auch die modul-level Dekoratoren wieder benutzen.
Middleware-Unterstützung ist rudimentär durch run(app=MiddleWare(default_app())) gegeben. Mir schwebt aber eher sowas wie bottle.middleware.append(MiddleWare) vor. Mal sehen, das schau ich mir morgen an

Verfasst: Sonntag 26. Juli 2009, 12:13
von Dauerbaustelle
Defnull hat geschrieben:Ich würde aber eher match_url wrappen:
Code: Alles auswählen
class MyBoddle(Bottle):
def match_url(self, url, *args, **kargs):
return Bottle.match_url(self, url.rstrip('/'), *args, **kargs)
default_app(MyBoddle())
*Exakt* das war mein Code, du Dieb! :D Außer `default_app(foobar)`. Aber dann kann ich das ja jetzt einbauen.
Verfasst: Sonntag 26. Juli 2009, 12:39
von snafu
Die Bottle-Version von easy_install hängt ganz schön hinterher. Zudem meldet er Fehler:
Code: Alles auswählen
$ sudo easy_install bottle
Processing bottle
Running setup.py -q bdist_egg --dist-dir /home/sebastian/bottle/egg-dist-tmp-VtNT2F
warning: no files found matching 'docs/*.html'
zip_safe flag not set; analyzing archive contents...
Removing bottle 0.4.11 from easy-install.pth file
Adding bottle 0.4.14 to easy-install.pth file
Installed /usr/local/lib/python2.6/dist-packages/bottle-0.4.14-py2.6.egg
Processing dependencies for bottle==0.4.14
Finished processing dependencies for bottle==0.4.14
EDIT: Die erste Warnung lässt sich beheben, wenn man `python bottle/docs/update.py` ausführt.
Verfasst: Sonntag 26. Juli 2009, 13:26
von Defnull
Ich pack nicht jede Version ins PyPi, sondern nur die, die dringende bugfixes enthalten oder wirklich neue Features bringen. Ob ich hinter den Kulissen etwas geändert habe, interessiert die normalen Anwender ja nicht. Im PyPi ist aber schon seit gestern die 0.5.3. Vielleicht mal mit "easy_install -U bottle" probieren?
Die Warnung find ich seltsam. In meinem Release-Script sollte update.py eigentlich ausgeführt werden. Wahrscheinlich landen die HTML-Dateien aber im falschen Verzeichnis...
Distutils muss ich mir eh nochmal genauer ansehen. Erscheint mir alles sehr unausgegoren. Besonders der Split zwischen distutils und setuptools ist verwirrend. Was is nu besser?
@Dauerbaustelle
Ne Middleware wäre dafür aber toller laut WSGI Philosophie:
Code: Alles auswählen
class PathUnifier(object):
def __init__(self, wrap):
self.wrap = wrap
def __call__(self, environ, start_response):
environ['PATH_INFO'] = environ.get('PATH_INFO', '/').rstrip('/')
return self.wrap(environ, start_response)
bottle.run(PathUnifier(bottle.default_app()))
Verfasst: Sonntag 26. Juli 2009, 13:48
von lunar
@defnull: Wenn du nicht auf Entry Points für ein Plugin-System oder auf die automatische Installation von Abhängigkeiten angewiesen bist, reichen die "distutils" eigentlich aus.