Bottle: Micro Web Framework

Stellt hier eure Projekte vor.
Internetseiten, Skripte, und alles andere bzgl. Python.
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

ice2k3 hat geschrieben: Edit: Oder kann ich wenigstens tief verschachtelte Ordnerstrukturen mit einer einzigen Route definieren?
Klar geht das.

Code: Alles auswählen

@route('/static/:path#.*#')
def send_static(path):
    return static_file(path, root='app\gwt\standard\')
Damit geht auch /static/images/ie6/icons/image.png problemlos. Für die Sonderfälle favicon.ico und robots.txt kann man ja ne extra route anlegen:

Code: Alles auswählen

@route('/:filename#favicon\.ico|robots\.txt#')
def send_special(filename):
    return static_file(filename, root='app\static\')
Aber bei einer aufgeräumten Pfad-Hierarchie sollte das eh die Ausnahme sein.
Bottle: Micro Web Framework + Development Blog
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

Defnull hat geschrieben:Aber bei einer aufgeräumten Pfad-Hierarchie sollte das eh die Ausnahme sein.
Beim GWT/GXT-Compiler hab ich leider keinen Einfluss auf die Pfad-Hierarchie ;)
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

ice2k3 hat geschrieben:Beim GWT/GXT-Compiler hab ich leider keinen Einfluss auf die Pfad-Hierarchie ;)
Doch, natürlich. Da kannst du genauso jede beliebige URL benutzen und XML oder JSON erfragen, wie auch mit jedem anderen Framework. Gerade bei GXT musst du die richtigen Store-Objekte (oder wie das da hieß) definieren und dann geht vieles für Tabellen wie von selbst.

Stefan
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

sma hat geschrieben:
ice2k3 hat geschrieben:Beim GWT/GXT-Compiler hab ich leider keinen Einfluss auf die Pfad-Hierarchie ;)
Doch, natürlich. Da kannst du genauso jede beliebige URL benutzen und XML oder JSON erfragen, wie auch mit jedem anderen Framework. Gerade bei GXT musst du die richtigen Store-Objekte (oder wie das da hieß) definieren und dann geht vieles für Tabellen wie von selbst.

Stefan
Ja, schon klar, dein Kommentar gilt für die Ajax-Requests...

Mein Kommentar bezieht sich allerdings auf die kompilierte UI, die ich mit bottle bereitstellen will. Und da hab ich nunmal diese Ordnerstruktur ;)

Ja, ich weiß, für einen Production-Einsatz ist das bestimmt suboptimal, wenn ich die ganzen static files über bottle bereitstelle (oder?).
Später sollte ich die direkt mit dem Webserver bereitstellen.

Edit: Wenn wir schon beim Thema sind, JSON dürfte deutlich schneller sein als Austauschformat, sowohl das Generieren beim (Python-)Server als auch das Parsen beim (JS-)Client, oder?
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Nicht böse gemeint, aber muss man damit jetzt unbedingt den Bottle-Thread zukleistern?
Benutzeravatar
webwurst
User
Beiträge: 7
Registriert: Sonntag 7. Februar 2010, 02:23

Ich bekomme bottle.py grad nicht zum laufen..

Habe die Version von heute und wenn ich das mit dem "Hello World"-Beispiel aus der 0.7er Doku starte, dann bekomme ich folgendes:

Code: Alles auswählen

Bottle server starting up (using AutoServer())...
Listening on http://127.0.0.1:8080/
Use Ctrl-C to quit.

Traceback (most recent call last):
  File "hello.py", line 5, in <module>
    run() # This starts the HTTP server
  File "/home/webwurst/Development/bottle/bottle.py", line 1246, in run
    server.run(app)
  File "/home/webwurst/Development/bottle/bottle.py", line 1219, in run
    return sa(self.host, self.port, **self.options).run(handler)
  File "/home/webwurst/Development/bottle/bottle.py", line 1193, in run
    reactor.listenTCP(self.port, self.host)
  File "/usr/lib/python2.6/dist-packages/twisted/internet/posixbase.py", line 356, in listenTCP
    p.startListening()
  File "/usr/lib/python2.6/dist-packages/twisted/internet/tcp.py", line 868, in startListening
    self.factory.doStart()
AttributeError: 'str' object has no attribute 'doStart'
[/code]
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

Der TwistedAdapter war irgentwie kaputt. Komisch das das bisher niemanden aufgefallen ist, den nutzt wohl keiner freiwillig.

Neu ist nämlich, das nicht mehr WSGIRefServer der Standard ist, sondern AutoServer, der selbständig nach einem besseren installierten Server-Modul sucht und bei dir wohl Twisted gefunden hat.

Ob dieser Standard aber wirklich so gut ist, wie ich dachte, als ich das implementiert habe, weiß ich nicht mehr so recht.
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
webwurst
User
Beiträge: 7
Registriert: Sonntag 7. Februar 2010, 02:23

Jau, besten Dank! So tut's schonmal.

Sobald ich allerdings den autreloader nutze mit run(reloader=True), dann erhalte ich folgendes:

Code: Alles auswählen

Bottle auto reloader starting up...
Bottle server starting up (using AutoServer())...
Listening on http://127.0.0.1:8080/
Use Ctrl-C to quit.

Traceback (most recent call last):
  File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 374, in fireEvent
    DeferredList(beforeResults).addCallback(self._continueFiring)
  File "/usr/lib/python2.6/dist-packages/twisted/internet/defer.py", line 195, in addCallback
    callbackKeywords=kw)
  File "/usr/lib/python2.6/dist-packages/twisted/internet/defer.py", line 186, in addCallbacks
    self._runCallbacks()
  File "/usr/lib/python2.6/dist-packages/twisted/internet/defer.py", line 328, in _runCallbacks
    self.result = callback(self.result, *args, **kw)
--- <exception caught here> ---
  File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 387, in _continueFiring
    callable(*args, **kwargs)
  File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 1123, in _reallyStartRunning
    self._handleSignals()
  File "/usr/lib/python2.6/dist-packages/twisted/internet/base.py", line 1068, in _handleSignals
    signal.signal(signal.SIGINT, self.sigInt)
exceptions.ValueError: signal only works in main thread
zoxzox3
User
Beiträge: 14
Registriert: Dienstag 26. Januar 2010, 18:21

POST is empty, bottle 0.8

Code: Alles auswählen

from bottle import request, response, run, route

@route('/')
def cookie():
    return '<html><body> <form method=post> <input type=text name=name /> <input type=submit /> </form> </body></html>'

@route('/', method='POST')
def set_cookie():
    if 'name' in request.POST: return 'ok'
    return 'error'

run(port=8080)
after post return 'error',

in bottle 0.7.0a, it work ok
ms4py
User
Beiträge: 1178
Registriert: Montag 19. Januar 2009, 09:37

zoxzox3 hat geschrieben:in bottle 0.7.0a, it work ok
Really? Your HTML form is flawed, the "action" parameter is required.
See: http://www.w3.org/TR/html401/interact/forms.html#h-17.3
„Lieber von den Richtigen kritisiert als von den Falschen gelobt werden.“
Gerhard Kocher

http://ms4py.org/
zoxzox3
User
Beiträge: 14
Registriert: Dienstag 26. Januar 2010, 18:21

ms4py hat geschrieben:
zoxzox3 hat geschrieben:in bottle 0.7.0a, it work ok
Really? Your HTML form is flawed, the "action" parameter is required.
See: http://www.w3.org/TR/html401/interact/forms.html#h-17.3
If "action" is not specified then all browsers are perceived as the current url.
is not being, post in the bottle is empty.
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

zoxzox3 hat geschrieben:If "action" is not specified then all browsers are perceived as the current url.
No. The HTTP standard says you have specificy the 'action' parameter explicitly.
zoxzox3
User
Beiträge: 14
Registriert: Dienstag 26. Januar 2010, 18:21

POST is empty, bottle 0.8

Code: Alles auswählen

from bottle import request, response, run, route

@route('/')
def cookie():
    return '<html><body> <form action="/" method=post> <input type=text name=name /> <input type=submit /> </form> </body></html>'

@route('/', method='POST')
def set_cookie():
    if 'name' in request.POST: return 'ok'
    return 'error'

run(port=8080)
after post return 'error',

in bottle 0.7.0a, it work ok
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

I recommend you writing valid HTML code. It's 'method="POST"' rather than 'method=post'.
zoxzox3
User
Beiträge: 14
Registriert: Dienstag 26. Januar 2010, 18:21

POST is empty, bottle 0.8

Code: Alles auswählen

from bottle import request, response, run, route 

@route('/') 
def cookie(): 
    return '<html><body> <form action="/" method="POST" > <input type="TEXT" name="name" /> <input type="submit" /> </form> </body></html>' 

@route('/', method='POST') 
def set_cookie(): 
    if 'name' in request.POST: return 'ok' 
    return 'error' 

run(host='', port=8080)
after post return 'error',

in bottle 0.7.0a, it work ok

ps: if you do not know what the problem is silent
Dauerbaustelle
User
Beiträge: 996
Registriert: Mittwoch 9. Januar 2008, 13:48

zoxzox3 hat geschrieben:ps: if you do not know what the problem is silent
Yeah. Because you're the one who gives orders here.

You shouldn' expect someone cares about that bug if you can't set up a should-work example (what you finally did with your latest post).
apollo13
User
Beiträge: 827
Registriert: Samstag 5. Februar 2005, 17:53

zoxzox3 hat geschrieben:POST is empty, bottle 0.8

Code: Alles auswählen

from bottle import request, response, run, route 

@route('/') 
def cookie(): 
    return '<html><body> <form action="/" method="POST" > <input type="TEXT" name="name" /> <input type="submit" /> </form> </body></html>' 

@route('/', method='POST') 
def set_cookie(): 
    if 'name' in request.POST: return 'ok' 
    return 'error' 

run(host='', port=8080)
after post return 'error',

in bottle 0.7.0a, it work ok

ps: if you do not know what the problem is silent
Just disable the Twisted server, defnull added it recently but it still has some bugs (at least that's what I guess). And now go back to your troll home again… You could have easily figured that out yourself…
Benutzeravatar
Defnull
User
Beiträge: 778
Registriert: Donnerstag 18. Juni 2009, 22:09
Wohnort: Göttingen
Kontaktdaten:

This is a known issue. I cannot reproduce it yet but I think it is caused by twisted handling more than one request at the same time per thread. Bottle relies on threading.local to work, which is not the case with some asynchronous server implementations.

http://github.com/defnull/bottle/issues#issue/45
Bottle: Micro Web Framework + Development Blog
Benutzeravatar
noisefloor
User
Beiträge: 4175
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

Anmerkung zu @validate:

Ich finde die Doku (nicht das Tutorial :D ) hier missverständlich. IMHO geht daraus nicht hervor, dass Bottle bei erfolgreicher Validierung direkt den Typ ändert, von String in was-auch immer.

Beispiel:

Code: Alles auswählen

#!/usr/bin/env python

from bottle import route, validate, run, debug

@route('valid/:nr')
@validate(nr=int)
def valid(nr):
    if isinstance(nr, int):
        return 'Zahl'
    else:
        return 'Keine Zahl'

@route('no_valid/:nr')
def no_valid(nr):
    if isinstance(nr, int):
        return 'Zahl'
    else:
        return 'Keine Zahl'

debug(True)        
run(reloader=True)
Der Aufruf von "http://localhost:8080/valid/1" ergibt "Zahl", der Aufruf von "http://localhost:8080/no_valid/1" ergibt "keine Zahl".

Nicht schlimm ;-), sollte aber explizit erwähnt werden.

Gruß, noisefloor
Benutzeravatar
snafu
User
Beiträge: 6850
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Ich würde von einer Validierungsfunktion auch nicht erwarten, dass sie Dinge verändert, sondern nur, dass sie Laut gibt, wenn etwas nicht stimmt. Wenn etwas angepasst übergeben werden soll, wäre wohl `convert` als Name besser geeignet. Überhaupt sehe ich persönlich aber eher wenig Mehrgewinn in dem besagten Decorator, aber das mag Geschmackssache sein.
Antworten