Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Keystone: Python Web Development, Simplified

dcrosta
March 20, 2012

Keystone: Python Web Development, Simplified

Presentation on Keystone, a simple Python web framework, to the NYC Python Meetup. March 20, 2012.

dcrosta

March 20, 2012
Tweet

More Decks by dcrosta

Other Decks in Programming

Transcript

  1. Define: Pitfall • Mixed logic + markup • No namespaces

    • Performance issues • Security is an afterthought • Dollar signs
  2. Define: Easy • Drop-in upgrades • No URL mapping •

    Run everywhere • Progressive enhancement • No setup*
  3. Keystone by Example {% set greeting = "Hello" %} <!doctype

    html> <html> <head> <title>{{greeting}} from Keystone</title> </head> <body> <h1>Hello, world</h1> </body> </html>
  4. Keystone by Example import random greeting = random.choice(['Hello', 'Goodbye']) name

    = random.choice(['World', 'Moon']) ---- <!doctype html> <html> <head> <title>{{greeting}} from Keystone</title> </head> <body> <h1>{{greeting}}, {{name}}</h1> </body> </html>
  5. Keystone by Example import pymongo db = pymongo.Connection()['test'] rslt =

    db.greetings.find_one() ---- <!doctype html> <html> <head> <title>{{rslt.greeting}} from Keystone</title> </head> <body> <h1>{{rslt.greeting}}, {{rslt.name}}</h1> </body> </html>
  6. Keystone by Example import pymongo, gridfs db = pymongo.Connection()['test'] storage

    = gridfs.GridFS() fileobj = gridfs.get_latest_version('image.png') header('Content-Type', 'image/png') return_response(fileobj) ----
  7. Paths are URLs + $APP/ + about.html + index.ks +

    static/ | + style.css | + script.js + %username.ks + %username/ + profile.ks /about.html /, /index /static/style.css /static/script.js /dcrosta /dcrosta/profile
  8. Paths are URLs + $APP/ + about.html + index.ks +

    static/ | + style.css | + script.js + %username.ks + %username/ + profile.ks /about.html /, /index /static/style.css /static/script.js /dcrosta /dcrosta/profile
  9. Paths are URLs + $APP/ + about.html + index.ks +

    static/ | + style.css | + script.js + %username.ks + %username/ + profile.ks /about.html /, /index /static/style.css /static/script.js /dcrosta /dcrosta/profile
  10. Paths are URLs + $APP/ + about.html + index.ks +

    static/ | + style.css | + script.js + %username.ks + %username/ + profile.ks /about.html /, /index /static/style.css /static/script.js /dcrosta /dcrosta/profile
  11. Paths are URLs + $APP/ + about.html + index.ks +

    static/ | + style.css | + script.js + %username.ks + %username/ + profile.ks /about.html /, /index /static/style.css /static/script.js /dcrosta /dcrosta/profile
  12. Paths are URLs + $APP/ + about.html + index.ks +

    static/ | + style.css | + script.js + %username.ks + %username/ + profile.ks /about.html /, /index /static/style.css /static/script.js /dcrosta /dcrosta/profile
  13. Paths are URLs + $APP/ + about.html + index.ks +

    static/ | + style.css | + script.js + %username.ks + %username/ + profile.ks /about.html /, /index /static/style.css /static/script.js /dcrosta /dcrosta/profile
  14. Behind the Scenes def parse(self, fileobj): first, second = [],

    [] active = first for lineno, line in enumerate(fileobj): if line.strip() == '----': if active is second: raise InvalidTemplate( 'Line %d: separator already seen' % lineno) active = second else: active.append(line) viewcode_str = ''.join(first) viewcode, viewglobals = self.compile(viewcode_str, fileobj.name) def viewfunc(viewlocals): exec viewcode in viewglobals, viewlocals return viewlocals return viewfunc
  15. Behind the Scenes def compile(self, viewcode_str, filename): viewcode = compile(viewcode_str,

    filename, 'exec') viewglobals = {} for stmt in compiler.parse(viewcode_str).node: if isinstance(stmt, Import): modname, asname = stmt.names[0] if asname is None: asname = modname viewglobals[asname] = __import__(modname) # else ... return viewcode, viewglobals
  16. Behind the Scenes def compile(self, viewcode_str, filename): viewcode = compile(viewcode_str,

    filename, 'exec') viewglobals = {} for stmt in compiler.parse(viewcode_str).node: # if ... elif isinstance(stmt, From): fromlist = [x[0] for x in stmt.names] mod = __import__(stmt.modname, {}, {}, fromlist) for name, asname in stmt.names: if name == '*': for asname in getattr(mod, '__all__', dir(mod)): viewglobals[asname] = getattr(mod, asname) else: if asname is None: asname = name viewglobals[asname] = getattr(mod, name) return viewcode, viewglobals
  17. Behind the Scenes def render_keystone(self, request, template): response = Response(mimetype='text/html')

    viewlocals = { 'request': request, 'headers': response.headers, # ... } try: response.response = self.engine.render(template, viewlocals) except HTTPException, ex: return ex.get_response(request.environ) return response def render(self, template, viewlocals): jinja_template = jinja_env.get_template(template.name) return jinja_template.generate(**template.viewfunc(viewlocals))
  18. Other Goodies • PaaS support keystone --configure {heroku,dotcloud,epio} • Run

    code at startup startup.py • HTTP Methods request.method == "POST" • HTTP Errors raise NotFound() • Cookies set_cookie(), delete_cookie()
  19. Get Keystone • PyPI pip install Keystone • Source at

    Github https://github.com/dcrosta/keystone • Documentation http://keystone.readthedocs.org/