Slide 1

Slide 1 text

ABOUT DATE BY 2012-03-20 DAN CROSTA (@LAZLOFRUVOUS) KEYSTONE PYTHON WEB DEVELOPMENT, SIMPLIFIED

Slide 2

Slide 2 text

Question: Can Python be as easy as PHP?

Slide 3

Slide 3 text

Question: ... without the pitfalls?

Slide 4

Slide 4 text

Define: Pitfall • Mixed logic + markup • No namespaces • Performance issues • Security is an afterthought • Dollar signs

Slide 5

Slide 5 text

Define: Easy • Drop-in upgrades • No URL mapping • Run everywhere • Progressive enhancement • No setup*

Slide 6

Slide 6 text

Keystone: All Python up front, HTML party in the back.

Slide 7

Slide 7 text

Keystone by Example Hello from Keystone

Hello, world

Slide 8

Slide 8 text

Keystone by Example {% set greeting = "Hello" %} {{greeting}} from Keystone

Hello, world

Slide 9

Slide 9 text

Keystone by Example import random greeting = random.choice(['Hello', 'Goodbye']) name = random.choice(['World', 'Moon']) ---- {{greeting}} from Keystone

{{greeting}}, {{name}}

Slide 10

Slide 10 text

Keystone by Example import pymongo db = pymongo.Connection()['test'] rslt = db.greetings.find_one() ---- {{rslt.greeting}} from Keystone

{{rslt.greeting}}, {{rslt.name}}

Slide 11

Slide 11 text

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) ----

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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))

Slide 23

Slide 23 text

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()

Slide 24

Slide 24 text

Get Keystone • PyPI pip install Keystone • Source at Github https://github.com/dcrosta/keystone • Documentation http://keystone.readthedocs.org/