Die SimpleTemplate Engine interpretiert so gut wie nichts. %def wird direkt als python-Keyword übernommen und definiert eine Funktion, wie man es von Python kennt. Das von dir gezeigte Beispiel sollte eigentlich so funktionieren, wie man es erwartet*. Wobei ich mit Variablen-Namen, die Buildins überschreiben ('id'), vorsichtig wäre.
*Allerdings erst ab commit 8668a763 auch über %rebase oder %include Grenzen hinweg. Im Release 0.6.3 können Funktionen nur innerhalb des selben Templates benutzt werden, in dem sie definiert wurden, da bei der Übergabe des Funktions-Objektes als Parameter bei %include oder $rebase der Kontext verloren ging. Die aktuelle GitHub Version behebt den Fehler.
Was das escapen an geht: '%' wird nur als Befehl erkannt, wenn es das erste nicht-Whitepace-Zeichen der Zeile ist. Mitten im Code kannst du '%' ohne Probleme verwenden. '{{}}' sollte eigentlich relativ selten in HTML Code vor kommen, daher hab ich dafür keine spezielle Escape-Möglichkeit ein gebaut. {{'%'}} und {{'{''{'}} funktioniert natürlich, auch wenn es nicht so schön aus sieht.
@Jija2: Das wird teil von 0.6.4, aber das kann noch ne wocher oder zwei dauern. Nimm die http://github.com/defnull/bottle/raw/master/bottle.py version.
Bottle: Micro Web Framework
- Defnull
- User
- Beiträge: 778
- Registriert: Donnerstag 18. Juni 2009, 22:09
- Wohnort: Göttingen
- Kontaktdaten:
Bottle: Micro Web Framework + Development Blog
Ich war mal so frei für die Git Version von bottle ein PKGBUILD zu schreiben und ins AUR zu schieben. Arch User können jetzt die Git Version mit ``yaourt -S python-bottle-git`` installieren, ein python-bottle gibt es ja erfreulicherweise schon seit einiger Zeit.
- Defnull
- User
- Beiträge: 778
- Registriert: Donnerstag 18. Juni 2009, 22:09
- Wohnort: Göttingen
- Kontaktdaten:
Sehr cool
Danke!
Ich hab inzwischen die Test Code Abdeckung auf 85% hoch bekommen. Das sind immer noch nicht 100%, aber der Reloader lässt sich nun mal nur sehr schwer testen (da er neue Prozesse auf macht), genau wie die meisten ServerAdapter noch nicht abgedeckt sind. Der Kern von Bottle ist aber inzwischen fast vollständig durch Tests abgedeckt.
Neu ist auch das automatische Unicode-Encoding. Besonders für Python3 oder Template-Engines, die unicode zurück geben, erspart das Einiges an Kopfschmerzen. UTF8 ist default, aber sobald man Content-Type neu setzt und dort einen charset an gibt oder bottle.response.charset direkt editiert, wird ein anderer default angewendet.
Was gibts noch neues? Achja: HEAD Requests werden jetzt auf GET umgemünzt, wenn es keine passende HEAD-Route gibt. In jedem Fall wird die Ausgabe vom HTTP-Body unterdrückt, damit HEAD auch klappt, wenn der ServerAdapter mist baut (wie es die meisten tun). Man kann das mit default_app().clearhead=False auch wieder aus schalten (z.B. wenn man gzip-middleware benutzt, die Content-Length neu berechnen muss).

Ich hab inzwischen die Test Code Abdeckung auf 85% hoch bekommen. Das sind immer noch nicht 100%, aber der Reloader lässt sich nun mal nur sehr schwer testen (da er neue Prozesse auf macht), genau wie die meisten ServerAdapter noch nicht abgedeckt sind. Der Kern von Bottle ist aber inzwischen fast vollständig durch Tests abgedeckt.
Neu ist auch das automatische Unicode-Encoding. Besonders für Python3 oder Template-Engines, die unicode zurück geben, erspart das Einiges an Kopfschmerzen. UTF8 ist default, aber sobald man Content-Type neu setzt und dort einen charset an gibt oder bottle.response.charset direkt editiert, wird ein anderer default angewendet.
Was gibts noch neues? Achja: HEAD Requests werden jetzt auf GET umgemünzt, wenn es keine passende HEAD-Route gibt. In jedem Fall wird die Ausgabe vom HTTP-Body unterdrückt, damit HEAD auch klappt, wenn der ServerAdapter mist baut (wie es die meisten tun). Man kann das mit default_app().clearhead=False auch wieder aus schalten (z.B. wenn man gzip-middleware benutzt, die Content-Length neu berechnen muss).
Bottle: Micro Web Framework + Development Blog
Etwas ganz banales (Version 0.6.3):
bottle.py Zeilen 489 ff. Funktion "send_file"
os.path.abspath erzeugt auf Windowsmaschinen Pfadangaben (Zeichenkette) mit Backslashes (\). Das Anhängen eines Slashes (/) führt dazu, dass die Bedingung 'if not filename.startswith(root):' niemals False wird und die Anfrage von statischen Dateien erfolglos bleibt (Access Denied).
Ich habe das jetzt insoweit umgangen, als dass '/' einfach nicht mehr angehangen wird. Zumal dies für das Erzeugen von 'filename' mittels 'os.path.join' nicht benötigt wird.
Ausschnitt:
Grüße... Heiko
bottle.py Zeilen 489 ff. Funktion "send_file"
os.path.abspath erzeugt auf Windowsmaschinen Pfadangaben (Zeichenkette) mit Backslashes (\). Das Anhängen eines Slashes (/) führt dazu, dass die Bedingung 'if not filename.startswith(root):' niemals False wird und die Anfrage von statischen Dateien erfolglos bleibt (Access Denied).
Ich habe das jetzt insoweit umgangen, als dass '/' einfach nicht mehr angehangen wird. Zumal dies für das Erzeugen von 'filename' mittels 'os.path.join' nicht benötigt wird.
Ausschnitt:
Code: Alles auswählen
def send_file(filename, root, guessmime = True, mimetype = None):
""" Aborts execution and sends a static files as response. """
root = os.path.abspath(root)
print('[DBG] send_file, root = {0}'.format(root))
filename = os.path.abspath(os.path.join(root, filename.strip('/')))
print('[DBG] send_file, filename = {0}'.format(filename))
# (...)
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
- Defnull
- User
- Beiträge: 778
- Registriert: Donnerstag 18. Juni 2009, 22:09
- Wohnort: Göttingen
- Kontaktdaten:
Ohne das Anhängen von '/' wäre folgendes möglich:
Ich hab es jetzt in so weit Windows kompatibel gemacht, das statt '/' os.sep angefügt wird und das strip in der nächsten Zeile auch Backslashes entfernt.
Code: Alles auswählen
>>> root = '/save/path/'
>>> filename = '../path2/unsave'
>>> root = os.path.abspath(root)
>>> filename = os.path.abspath(os.path.join(root, filename.strip('/')))
>>> root
'/save/path'
>>> filename
'/save/path2/unsave'
>>> filename.startswith(root)
True
Bottle: Micro Web Framework + Development Blog
Soweit habe ich noch nicht geschaut/gedacht. Mir war erstmal wichtig, dass ich überhaupt statischen Inhalt servieren konnte.Defnull hat geschrieben:Ohne das Anhängen von '/' wäre folgendes möglich:
(...)

(os.sep .. wieder was gelernt)
Weiterhin viel Erfolg, tolles Projekt.
Grüße... Heiko
"Du bist der Messias! Und ich muss es wissen, denn ich bin schon einigen gefolgt!"
- Defnull
- User
- Beiträge: 778
- Registriert: Donnerstag 18. Juni 2009, 22:09
- Wohnort: Göttingen
- Kontaktdaten:
Gerade ist ein neuer Release draußen. Da die meisten Änderungen Bugfixes sind, empfehle ich das Update eigentlich jedem, der momentan mit 0.6.3 arbeitet. Wie bei jedem beta-Projekt ist natürlich weiterhin Vorsicht geboten, sollte man damit was produktives realisieren wollen 

Bottle: Micro Web Framework + Development Blog
-
- User
- Beiträge: 996
- Registriert: Mittwoch 9. Januar 2008, 13:48
Hab mal das mit dem Parent-Child-Terminate-Killen implementiert. Github spinnt grade, konnte nicht forken. Hier ist der Patch http://paste.pocoo.org/show/146426/ und in dauerbaustelle/bottle-temp ist meine Version.
Gruß
Gruß
- Defnull
- User
- Beiträge: 778
- Registriert: Donnerstag 18. Juni 2009, 22:09
- Wohnort: Göttingen
- Kontaktdaten:
Ich hab ausgehend von deiner Idee den Reloader nochmal komplett umstrukturiert. Er funktioniert jetzt so:
Der Hauptprozess startet keinen Server, sondern erschafft einen Kindprozess (mit identischen Komandozeilen-Argumenten), der diese Aufgabe übernimmt. Dann beginnt der Hauptprozess, die Dateien der geladenen Module auf Änderungen zu kontrollieren. Sobald sich etwas ändert, wird der Kindprozess getötet und ein Neuer gestartet. Das hat gegenüber der alten Lösung den Vorteil, das keine Threads benutzt werden müssen, was regelmäßig zu Problemen geführt hat.
Beendet sich der Kindprozess selbst, tut der Hauptprozess das gleiche. Stirbt der Hauptprozess unerwartet (SIGTERM), wir der Kindprozess mitgerissen. So bleiben keine Prozess-Leichen zurück. Auch das war vorher ein Problem.
Auf Systemen, die es unterstützen, wir der Kindprozess mit SIGINT beendet, was einem KeyboardInterrupt gleich kommt. Auf allen anderen Plattformen wird SIGTERM benutzt. Das ist zwar nicht besonders nett, aber ich sehe keinen anderen Weg (ausser IPC, aber das wird zu kompliziert) den Kindprozess sauber zu beenden und dabei auch Windows zu unterstützen.
Den neuen Ansatz findet man im 'shutdown' branch.
Der Hauptprozess startet keinen Server, sondern erschafft einen Kindprozess (mit identischen Komandozeilen-Argumenten), der diese Aufgabe übernimmt. Dann beginnt der Hauptprozess, die Dateien der geladenen Module auf Änderungen zu kontrollieren. Sobald sich etwas ändert, wird der Kindprozess getötet und ein Neuer gestartet. Das hat gegenüber der alten Lösung den Vorteil, das keine Threads benutzt werden müssen, was regelmäßig zu Problemen geführt hat.
Beendet sich der Kindprozess selbst, tut der Hauptprozess das gleiche. Stirbt der Hauptprozess unerwartet (SIGTERM), wir der Kindprozess mitgerissen. So bleiben keine Prozess-Leichen zurück. Auch das war vorher ein Problem.
Auf Systemen, die es unterstützen, wir der Kindprozess mit SIGINT beendet, was einem KeyboardInterrupt gleich kommt. Auf allen anderen Plattformen wird SIGTERM benutzt. Das ist zwar nicht besonders nett, aber ich sehe keinen anderen Weg (ausser IPC, aber das wird zu kompliziert) den Kindprozess sauber zu beenden und dabei auch Windows zu unterstützen.
Den neuen Ansatz findet man im 'shutdown' branch.
Bottle: Micro Web Framework + Development Blog
- Defnull
- User
- Beiträge: 778
- Registriert: Donnerstag 18. Juni 2009, 22:09
- Wohnort: Göttingen
- Kontaktdaten:
SIGINT produziert einen KeyboardInterrupt, welcher theoretisch abgefangen und verarbeitet werden kann, während SIGTERM den Programmfluss sofort unterbricht und alle finally-Blöcke und exit-Handler komplett ignoriert.
Bottle soll einfach sein. Man kann denke ich nicht erwarten, das jeder Bottle-Benutzer daran denkt, SIGTERM mit signal.signal() abzufangen, um seine File Objekte zu schließen. Da ist SIGINT die nettere Variante, finde ich. Und da SIGINT auch der normale Weg ist, einen Bottle Server zu beenden, sollte das auch zu einem sauberen Shutdown führen.
Bottle soll einfach sein. Man kann denke ich nicht erwarten, das jeder Bottle-Benutzer daran denkt, SIGTERM mit signal.signal() abzufangen, um seine File Objekte zu schließen. Da ist SIGINT die nettere Variante, finde ich. Und da SIGINT auch der normale Weg ist, einen Bottle Server zu beenden, sollte das auch zu einem sauberen Shutdown führen.
Bottle: Micro Web Framework + Development Blog
-
- User
- Beiträge: 5
- Registriert: Dienstag 13. Februar 2007, 17:05
Hallo zusammen,
ich beschäftige mich mich schon eine Weile mit Django und Werkzeug und verfolge den Thread schon eine Weile. Nun dachte ich spiele auch ein wenig mit bottle rum - vorab: der minimalistische Ansatz gefällt mir bisher sehr gut.
Aber nun etwas konstruktive Kritik
Als Template-Engine verwende ich zur Zeit Jinja2 und ich würde gerne den decorator jinja2_view verwenden. Wenn ich nun den kompletten Pfad angebe dann funktioniert das auch (z.B. @jinja2_view("/sehr/langer/pfad/templates/index.html)).
Allerdings wollte ich die Übersichtlichkeit etwas erhöhen indem ich per bottle.TEMPLATE_PATH.append('/sehr/langer/pfad/templates/') meinen eigenen Template-Pfad hinzufügen und dann @jinja2_view("index.html") aufrufe. Zur Zeit ist das allerdings so nicht möglich. Nach Studium des Quellcodes ist mir die Hardcodierung von .tpl aufgefallen, die schon mal meine index.html aus der automatischen Ergänzung der Pfade ausschließt, da immer dateiname + ".tpl" geprüft wird. Also habe ich zum Testen die Datei mal in index.tpl umbenannt. Ein @jinja2_view("index.tpl") folgt auch zum Fehler, da nun die automatische Pfadergänzung nicht mehr greift. Also @jinja2_view("index") versucht, das klappt endlich - allerdings gibt es nun ein Problem mit dem vererbten Template. Also das {% extends "base.tpl" %} in ein {% extends "base" %} geändert und es lieft.
Insgesamt habe ich es also zum Laufen bekommen, allerdings fand ich das vorgehen etwas unituitiv. Ich finde es eigenlich logischer irgendwo einen grundlegenden Template-Pfad festzulegen und dann per Dateinamen auf die entsprechenden templates zuzugreifen. Auch das feste .tpl finde ich nicht so optimal gelöst, aber ist wohl eher Geschmackssache.
Des Weiteren wäre eine etwas ausführlichere Fehlermeldung wenn das template nicht gefunden wird wünschenswert, d.h. also welche Pfade probiert wurden um das Template zu finden...
ich beschäftige mich mich schon eine Weile mit Django und Werkzeug und verfolge den Thread schon eine Weile. Nun dachte ich spiele auch ein wenig mit bottle rum - vorab: der minimalistische Ansatz gefällt mir bisher sehr gut.
Aber nun etwas konstruktive Kritik

Als Template-Engine verwende ich zur Zeit Jinja2 und ich würde gerne den decorator jinja2_view verwenden. Wenn ich nun den kompletten Pfad angebe dann funktioniert das auch (z.B. @jinja2_view("/sehr/langer/pfad/templates/index.html)).
Allerdings wollte ich die Übersichtlichkeit etwas erhöhen indem ich per bottle.TEMPLATE_PATH.append('/sehr/langer/pfad/templates/') meinen eigenen Template-Pfad hinzufügen und dann @jinja2_view("index.html") aufrufe. Zur Zeit ist das allerdings so nicht möglich. Nach Studium des Quellcodes ist mir die Hardcodierung von .tpl aufgefallen, die schon mal meine index.html aus der automatischen Ergänzung der Pfade ausschließt, da immer dateiname + ".tpl" geprüft wird. Also habe ich zum Testen die Datei mal in index.tpl umbenannt. Ein @jinja2_view("index.tpl") folgt auch zum Fehler, da nun die automatische Pfadergänzung nicht mehr greift. Also @jinja2_view("index") versucht, das klappt endlich - allerdings gibt es nun ein Problem mit dem vererbten Template. Also das {% extends "base.tpl" %} in ein {% extends "base" %} geändert und es lieft.
Insgesamt habe ich es also zum Laufen bekommen, allerdings fand ich das vorgehen etwas unituitiv. Ich finde es eigenlich logischer irgendwo einen grundlegenden Template-Pfad festzulegen und dann per Dateinamen auf die entsprechenden templates zuzugreifen. Auch das feste .tpl finde ich nicht so optimal gelöst, aber ist wohl eher Geschmackssache.
Des Weiteren wäre eine etwas ausführlichere Fehlermeldung wenn das template nicht gefunden wird wünschenswert, d.h. also welche Pfade probiert wurden um das Template zu finden...
-
- User
- Beiträge: 5
- Registriert: Dienstag 13. Februar 2007, 17:05
Gibt es eine Möglichkeit einen Wert zwischen unterschiedlichen Requests zu übergeben? Eigentlich hatte ich gedacht, dass es mit Cookies möglich sein sollte, aber so wie ich das sehe sind die cookies nur für den selben request abrufbar oder habe ich etwas übersehen?
-
- User
- Beiträge: 996
- Registriert: Mittwoch 9. Januar 2008, 13:48
Cookies speicherst du beim Client, d.h. die "Übergabe" des Wertes hängt davon ab, was der Client mit dem Cookie macht; im schlimmsten Fall ignoriert er es einfach und dein Wertübergeben funktioniert nicht.enlightenment hat geschrieben:aber so wie ich das sehe sind die cookies nur für den selben request abrufbar oder habe ich etwas übersehen?
Zuletzt geändert von Dauerbaustelle am Sonntag 1. November 2009, 14:04, insgesamt 1-mal geändert.
-
- User
- Beiträge: 5
- Registriert: Dienstag 13. Februar 2007, 17:05
ja, generell ist mir der Unterschied zwischen Cookie und Session schon klar. Und mir ist auch klar, dass es generell an den Nutzereinstellungen liegt ob Cookies erlaubt sind oder nicht.
Generell will ich einfach den eingeloggten User im Cookie zwischen speichern und dann auf die Unterseiten als eingeloggter User zugreifen.
Bei Werkzeug konnte ich für die gesamte Webseite auf den Cookie zugreifen konnte, d.h. nicht nur von dem Request von dem ich das Cookie gesetzt habe, deshalb frage ich mich wie man das idealerweise bei bottle realisieren kann.
Generell will ich einfach den eingeloggten User im Cookie zwischen speichern und dann auf die Unterseiten als eingeloggter User zugreifen.
Bei Werkzeug konnte ich für die gesamte Webseite auf den Cookie zugreifen konnte, d.h. nicht nur von dem Request von dem ich das Cookie gesetzt habe, deshalb frage ich mich wie man das idealerweise bei bottle realisieren kann.
-
- User
- Beiträge: 5
- Registriert: Dienstag 13. Februar 2007, 17:05
hm, vielleicht stehe ich ja gerade auf dem Schlauch - also hier mal ein einfaches Beispiel.
Mein Problem ist nun, wenn ich mich über login eingelogged habe, dann kann ich den username zwar beim erneuten Aufrufen von login anzeigen lassen, aber beim Aufruf von main ist das cookie-dictionary leer.
Code: Alles auswählen
@route("/")
def main():
print request.COOKIES.get("username", "")
@route("/user/login")
@route("/user/login", method="POST")
def login():
...
if request.method == "POST":
response.COOKIES['username'] = request.POST["username"]
else:
print request.COOKIES.get("username", "")
...
`bottle.redirect()` ist nicht dokumentiert. Zumindest finde ich es nicht auf http://bottle.paws.de/page/docs . Keine Support-Anfrage, sondern ein Hinweis. 

- Defnull
- User
- Beiträge: 778
- Registriert: Donnerstag 18. Juni 2009, 22:09
- Wohnort: Göttingen
- Kontaktdaten:
Cookies, die ohne 'path' Angabe gesetzt werden, gelten nur für das aktuelle und die darunter liegenden Verzeichnisse.enlightenment hat geschrieben:Mein Problem ist nun, wenn ich mich über login eingelogged habe, dann kann ich den username zwar beim erneuten Aufrufen von login anzeigen lassen, aber beim Aufruf von main ist das cookie-dictionary leer.
Code: Alles auswählen
bottle.response.COOKIES['username'] = 'defnull'
bottle.response.COOKIES['username']['path'] = '/'
# oder besser
bottle.response.set_cookie('username', 'defnull', path='/')
Bottle: Micro Web Framework + Development Blog
Hi,
ich teste auch grade deine Flasche
Dabei ist mir aufgefallen, dass die Doku zu Apache mod_wsgi einen Fehler enthält. Die ursprüngliche Anweisung
wirft einen NameError ( NameError: name '__FILE__' is not defined ). Wenn ich es so
schreibe (also klein), dann läuft es durch.
Gruß
Frank
ich teste auch grade deine Flasche

Dabei ist mir aufgefallen, dass die Doku zu Apache mod_wsgi einen Fehler enthält. Die ursprüngliche Anweisung
Code: Alles auswählen
os.chdir(os.path.dirname(__FILE__))
Code: Alles auswählen
os.chdir(os.path.dirname(__file__))
Gruß
Frank