This is a basic structure of what I started with:
import Bottle
app = Bottle.Bottle()
@app.route('/page')
def page():
# ...
return Bottle.template("views/page", text=text)
@app.route('/data')
def data():
# ...
return data # dict
if __name__ == '__main__':
Bottle.run(app, host='0.0.0.0', port=8080, server='paste')
Importing is the first step How can I change my imports to get the basics in without changing too much.
It’s more than just a s/Bottle/Klein
, though; the API interfaces are similar, but not identical.
-import Bottle
+import Klein
-app = Bottle.Bottle()
+app = Klein.Klein()
This is fine, but then you get issues when calling @app.get
, since that doesn’t exist.
-@app.get('/page')
-def page():
+@app.route('/page', methods=["GET"])
+def page(request):
Note the inclusion of request
; the route
isn’t a void method in Klein, but you don’t have to do anything with request
, so it’s just ‘syntaxic sugar` in a conversion.
The invocation of the server is just a small change, as well.
if __name__ == '__main__':
- Bottle.run(app, host='0.0.0.0', port=8080, server='paste')
+ app.run('0.0.0.0', 8080)
But then comes the replacement for the Bottle templates. Turns out that helpers are abound in Bottle, and I was using something that I picked up based on a sample app, not understanding the complexity of the templating underneath.
It look a bit of googling of the template syntax I was using to work out I was using Jinja under the hood. Now, the docs say that you can use Twisted.Web templates, but I have perfectly good templates that I want to use.
Turns out, I just need to explicitly import Jinja, and get that engine to process my templates, then pass that back into Klein.
- return Bottle.templates("views/page", text=text)
+ with open("views/page.tpl") as f:
+ t = Template(f.read())
+ return t.render(text=text)
This seemed very verbose to me, so what I did was make my own templater
helper as a replacement for Bottle.template
:
def templater(filename, **kwargs):
tpl = "%s.tpl" % filename
with open(tpl) as f:
t = Template(f.read())
return t.render(**kwargs)
So my actual change reduces down to:
- return Bottle.templates("views/page", text=text)
+ return templater("views/page", text=text)
That solves the templates. Next, static files. I had all my assets in /assets
on my filesystem, so it was a case of just serving this folder directly.
+@app.route('/assets/', branch=True)
+def static(request):
+ return File("./assets")
Finally, JSON data. By default, Bottle was transparently doing Content-Type
conversions for me, so I never had to worry. Klein got very upset when I tried to return a dict from a route, so I had to explicitly set some values.
- return data
+ request.setHeader('Content-Type', 'application/json')
+ return json.dumps(data)
Final summation: I ended up with:
import Klein
app = Klein.Klein()
def templater(filename, **kwargs):
tpl = "%s.tpl" % filename
with open(tpl) as f:
t = Template(f.read())
return t.render(**kwargs)
@app.rouet('/page', methods=["GET"])
def page(request):
# ...
return templater("views/page", text=text)
@app.get('/data')
def data():
# ...
request.setHeader('Content-Type', 'application/json')
return json.dumps(data)
if __name__ == '__main__':
app.run('0.0.0.0', 8080)
Your milage my vary, but this is all I had to do to get Bottle 0.12.9 into Klein 15.3.1.