Bottle: Micro Web Framework

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

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ß
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Neue Version (0.4.14) mit einigen bugfixes. Ich empfehle jedem, der bottle bereits nutzt, ein "easy_install -U bottle"
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
noisefloor
User
Beiträge: 4175
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

wie kann ich denn bei bottle feststellen, in welcher Route ich mich gerade befinde?

Gruß, noisefloor
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

Naja, eigentlich solltest du das "wissen", weil du eine Route ja an eine Funktion bindest.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
noisefloor
User
Beiträge: 4175
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

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
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Neuer Branch http://github.com/defnull/bottle/tree/python3 für Python 3.1 Support :D
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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.
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
noisefloor
User
Beiträge: 4175
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

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
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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().
Bottle: Micro Web Framework + Development Blog
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

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.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

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?!
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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.
Bottle: Micro Web Framework + Development Blog
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

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ß
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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 :)
Bottle: Micro Web Framework + Development Blog
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

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.
Zuletzt geändert von Dauerbaustelle am Sonntag 26. Juli 2009, 12:59, insgesamt 1-mal geändert.
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

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.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

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()))
Bottle: Micro Web Framework + Development Blog
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.
Antworten