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

Keystone: Python Web Development, Simplified

Avatar for dcrosta 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.

Avatar for dcrosta

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/