Pyramid:A Drive-by
Tutorial
Paul Everitt@dcpython - Feb 5 2013
Friday, February 8, 13
I Am...
• Prehistoric
• Web, Python, Zope, Plone, Pyramid
• Agendaless Consulting with Chris and Tres,
Fredericksburg
• Ma...
Why Pyramid?
Friday, February 8, 13
Friday, February 8, 13
Friday, February 8, 13
Friday, February 8, 13
What is Pyramid?
• Python (2 and 3) web framework
• Merger of repoze.bfg (from the Zope guys)
and Pylons
• Target audience...
Show Me The Money
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
from pyramid.respons...
What...what’s that print?
• print ('Starting up server on http://localhost:6547')
• Yep, we’re going to do this on Python ...
Goals of Pyramid
Friday, February 8, 13
Only Pay For WhatYou
Eat
• Easy to start
• Few choices forced on you
• Small codebase
Friday, February 8, 13
Quality
Friday, February 8, 13
Quality
• Full test and docs coverage (culture of docs
and tests)
• Performance, profiling
Friday, February 8, 13
Small to Big
• Starting point and finishing point
• Unique features that let you scale your
design (configuration, events, c...
Back to 3.3
Friday, February 8, 13
Setting UpYour
virtualenv
$ wget http://python-distribute.org/distribute_setup.py
$ pyvenv-3.3 env33
$ env33/bin/python3.3...
Installing and Running
Pyramid
$ env33/bin/pip-3.3 install pyramid
....chuggalugga...
$ env33/bin/python3.3 hello.py
Frida...
Boring
• Single-file “applications” are possible
• Just not the target
• Let’s do a simple package
Friday, February 8, 13
2: Hello Package
• setup.py
• tutorial/
• __init__.py
• helloworld.py
• views.py
Friday, February 8, 13
02: setup.py
from setuptools import setup
requires = [
'pyramid',
]
setup(name='tutorial',
entry_points="""
[paste.app_fac...
02: __ini__.py
# package
Friday, February 8, 13
02: helloworld.py
from wsgiref.simple_server import make_server
from pyramid.config import Configurator
def main():
config = ...
02: views
from pyramid.response import Response
from pyramid.view import view_config
@view_config(route_name='hello')
def he...
03: Pyramid
Configuration
• .ini files
• pserve (and friends)
Friday, February 8, 13
03: development.ini[app:main]
use = egg:tutorial
pyramid.reload_templates = true
[server:main]
use = egg:pyramid#wsgiref
h...
03: __init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator(settings=set...
04: Running
$ pserve development.ini --reload
Starting server in PID 32130.
Starting HTTP server on http://0.0.0.0:6547
12...
Yikes! Culture of
Testing!
• $ pip-3.3 install nose WebTest
Friday, February 8, 13
04: tests.py
import unittest
from pyramid import testing
classViewTests(unittest.TestCase):
def setUp(self):
self.config = ...
04:Test Running
$ nosetests .
..
----------------------------------------------------------------------
Ran 2 tests in 0.4...
05: Let’s Do a Template
• Change our view to use a “renderer”
• In this case, a template
• Makes test-writing better
• tut...
05: views.py
from pyramid.view import view_config
@view_config(route_name='hello', renderer='templates/helloworld.pt')
def h...
05: templates/helloworld.pt
<html>
<head>
<title>${title}</title>
</head>
<body>
<div>
<h1>${title}</h1>
</div>
</body>
</...
05: tests.py
import unittest
from pyramid import testing
classViewTests(unittest.TestCase):
def setUp(self):
self.config = ...
06:View Classes
Friday, February 8, 13
06: views.py
from pyramid.view import view_config
class HelloWorld(object):
def __init__(self, request):
self.request = req...
06: tests.py
def test_my_view(self):
from tutorial.views import HelloWorld
request = testing.DummyRequest()
inst = HelloWo...
07: Static Assets
• tutorial/static/
• hello.css
• logo.png
Friday, February 8, 13
07: __init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator(settings=set...
07: helloworld.pt
<html>
<head>
<title>${title}</title>
<link rel="stylesheet"
href="${request.static_url('tutorial:static...
08: Forms
• Use Deform for CRUD (with Colander and
Peppercorn)
• $ pip install deform
• deform_bootstrap optional
• Severa...
08: views.py
import colander
import deform
from pyramid.view import view_config
class Person(colander.MappingSchema):
name ...
08: helloworld.pt
<html>
<head>
<title>${title}</title>
<link rel="stylesheet"
href="${request.static_url('tutorial:static...
08: __init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
config = Configurator(settings=set...
09: FormValidation
Friday, February 8, 13
09: views.py
import colander
import deform
from pyramid.decorator import reify
from pyramid.view import view_config, view_d...
09: views.py
@view_defaults(route_name='hello',
renderer='templates/helloworld.pt')
class HelloWorld(object):
title = 'Hel...
09: helloworld.pt
<html>
<head>
<title>${view.title}</title>
<link rel="stylesheet"
href="${request.static_url('tutorial:s...
Validation
Friday, February 8, 13
...and more
• Other template languages
• SQLAlchemy (or others) for storage/
retrieval
• Authentication and authorization
...
Upcoming SlideShare
Loading in …5
×

Pyramid dc python feb 2013

2,189 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,189
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
22
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Pyramid dc python feb 2013

  1. 1. Pyramid:A Drive-by Tutorial Paul Everitt@dcpython - Feb 5 2013 Friday, February 8, 13
  2. 2. I Am... • Prehistoric • Web, Python, Zope, Plone, Pyramid • Agendaless Consulting with Chris and Tres, Fredericksburg • Manage large web projects...yep, I’m pimping Friday, February 8, 13
  3. 3. Why Pyramid? Friday, February 8, 13
  4. 4. Friday, February 8, 13
  5. 5. Friday, February 8, 13
  6. 6. Friday, February 8, 13
  7. 7. What is Pyramid? • Python (2 and 3) web framework • Merger of repoze.bfg (from the Zope guys) and Pylons • Target audience: Projects that start small and finish big Friday, February 8, 13
  8. 8. Show Me The Money from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response('Hello') def main(): config = Configurator() config.add_route('hello', '/') config.add_view(hello_world, route_name='hello') app = config.make_wsgi_app() return app if __name__ == '__main__': app = main() server = make_server('0.0.0.0', 6547, app) print ('Starting up server on http://localhost:6547') server.serve_forever() Friday, February 8, 13
  9. 9. What...what’s that print? • print ('Starting up server on http://localhost:6547') • Yep, we’re going to do this on Python 3.3 Tutorial walkthrough at: https://github.com/pauleveritt/dcpython Friday, February 8, 13
  10. 10. Goals of Pyramid Friday, February 8, 13
  11. 11. Only Pay For WhatYou Eat • Easy to start • Few choices forced on you • Small codebase Friday, February 8, 13
  12. 12. Quality Friday, February 8, 13
  13. 13. Quality • Full test and docs coverage (culture of docs and tests) • Performance, profiling Friday, February 8, 13
  14. 14. Small to Big • Starting point and finishing point • Unique features that let you scale your design (configuration, events, custom renderers, view predicates, traversal, ...) Friday, February 8, 13
  15. 15. Back to 3.3 Friday, February 8, 13
  16. 16. Setting UpYour virtualenv $ wget http://python-distribute.org/distribute_setup.py $ pyvenv-3.3 env33 $ env33/bin/python3.3 distribute_setup.py $ env33/bin/easy_install-3.3 pip Friday, February 8, 13
  17. 17. Installing and Running Pyramid $ env33/bin/pip-3.3 install pyramid ....chuggalugga... $ env33/bin/python3.3 hello.py Friday, February 8, 13
  18. 18. Boring • Single-file “applications” are possible • Just not the target • Let’s do a simple package Friday, February 8, 13
  19. 19. 2: Hello Package • setup.py • tutorial/ • __init__.py • helloworld.py • views.py Friday, February 8, 13
  20. 20. 02: setup.py from setuptools import setup requires = [ 'pyramid', ] setup(name='tutorial', entry_points=""" [paste.app_factory] main = tutorial:main """, ) Friday, February 8, 13
  21. 21. 02: __ini__.py # package Friday, February 8, 13
  22. 22. 02: helloworld.py from wsgiref.simple_server import make_server from pyramid.config import Configurator def main(): config = Configurator() config.add_route('hello', '/') config.scan('views') app = config.make_wsgi_app() return app if __name__ == '__main__': app = main() server = make_server('0.0.0.0', 6547, app) print ('Starting up server on http://localhost:6547') server.serve_forever() Friday, February 8, 13
  23. 23. 02: views from pyramid.response import Response from pyramid.view import view_config @view_config(route_name='hello') def hello_world(request): return Response('Hello') Friday, February 8, 13
  24. 24. 03: Pyramid Configuration • .ini files • pserve (and friends) Friday, February 8, 13
  25. 25. 03: development.ini[app:main] use = egg:tutorial pyramid.reload_templates = true [server:main] use = egg:pyramid#wsgiref host = 0.0.0.0 port = 6543 [loggers] keys = root [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [handler_console] class = StreamHandler args = (sys.stderr,) level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s Friday, February 8, 13
  26. 26. 03: __init__.py from pyramid.config import Configurator def main(global_config, **settings): config = Configurator(settings=settings) config.add_route('hello', '/') config.scan() return config.make_wsgi_app() Friday, February 8, 13
  27. 27. 04: Running $ pserve development.ini --reload Starting server in PID 32130. Starting HTTP server on http://0.0.0.0:6547 127.0.0.1 - - [03/Feb/2013 20:03:58] "GET / HTTP/1.1" 200 5 Friday, February 8, 13
  28. 28. Yikes! Culture of Testing! • $ pip-3.3 install nose WebTest Friday, February 8, 13
  29. 29. 04: tests.py import unittest from pyramid import testing classViewTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def test_my_view(self): from tutorial.views import hello_world request = testing.DummyRequest() response = hello_world(request) self.assertEqual(response.status, '200 OK') # ....con’t class FunctionalTests(unittest.TestCase): def setUp(self): from tutorial import main settings = {} app = main(settings) from webtest import TestApp self.testapp = TestApp(app) def test_it(self): res = self.testapp.get('/', status=200) self.assertIn(b'Hello', res.body) Friday, February 8, 13
  30. 30. 04:Test Running $ nosetests . .. ---------------------------------------------------------------------- Ran 2 tests in 0.498s OK Friday, February 8, 13
  31. 31. 05: Let’s Do a Template • Change our view to use a “renderer” • In this case, a template • Makes test-writing better • tutorial/templates/ • helloworld.pt Friday, February 8, 13
  32. 32. 05: views.py from pyramid.view import view_config @view_config(route_name='hello', renderer='templates/helloworld.pt') def hello_world(request): return dict(title='Hello World') Friday, February 8, 13
  33. 33. 05: templates/helloworld.pt <html> <head> <title>${title}</title> </head> <body> <div> <h1>${title}</h1> </div> </body> </html> Friday, February 8, 13
  34. 34. 05: tests.py import unittest from pyramid import testing classViewTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def test_my_view(self): from tutorial.views import hello_world request = testing.DummyRequest() response = hello_world(request) self.assertEqual(response['title'], 'Hello World') class FunctionalTests(unittest.TestCase): def setUp(self): from tutorial import main settings = {} app = main(settings) from webtest import TestApp self.testapp = TestApp(app) def test_it(self): res = self.testapp.get('/', status=200) self.assertIn(b'Hello World', res.body) Friday, February 8, 13
  35. 35. 06:View Classes Friday, February 8, 13
  36. 36. 06: views.py from pyramid.view import view_config class HelloWorld(object): def __init__(self, request): self.request = request @view_config(route_name='hello', renderer='templates/helloworld.pt') def hello_world(self): return dict(title='Hello World') Friday, February 8, 13
  37. 37. 06: tests.py def test_my_view(self): from tutorial.views import HelloWorld request = testing.DummyRequest() inst = HelloWorld(request) response = inst.hello_world() self.assertEqual(response['title'], 'Hello World') Friday, February 8, 13
  38. 38. 07: Static Assets • tutorial/static/ • hello.css • logo.png Friday, February 8, 13
  39. 39. 07: __init__.py from pyramid.config import Configurator def main(global_config, **settings): config = Configurator(settings=settings) config.add_route('hello', '/') config.add_static_view(name='static', path='tutorial:static') config.scan() return config.make_wsgi_app() Friday, February 8, 13
  40. 40. 07: helloworld.pt <html> <head> <title>${title}</title> <link rel="stylesheet" href="${request.static_url('tutorial:static/hello.css')}"/> </head> <body> <div> <h1>${title}</h1> <p> <img src="${request.static_url('tutorial:static/logo.png')}" alt="Logo"/> </p> </div> </body> </html> Friday, February 8, 13
  41. 41. 08: Forms • Use Deform for CRUD (with Colander and Peppercorn) • $ pip install deform • deform_bootstrap optional • Several other form libraries for Pyramid • This step is ONLY rendering Friday, February 8, 13
  42. 42. 08: views.py import colander import deform from pyramid.view import view_config class Person(colander.MappingSchema): name = colander.SchemaNode(colander.String()) age = colander.SchemaNode(colander.Integer(), validator=colander.Range(0, 200)) class HelloWorld(object): def __init__(self, request): self.request = request @view_config(route_name='hello', renderer='templates/helloworld.pt') def hello_world(self): schema = Person() form = deform.Form(schema, buttons=('submit',)) return dict(title='Hello World', form=form, reqts=form.get_widget_resources()) Friday, February 8, 13
  43. 43. 08: helloworld.pt <html> <head> <title>${title}</title> <link rel="stylesheet" href="${request.static_url('tutorial:static/hello.css')}"/> <tal:block repeat="reqt reqts['css']|[]"> <link rel="stylesheet" type="text/css" href="${request.static_url('deform:static/' + reqt)}"/> </tal:block> <tal:block repeat="reqt reqts['js']|[]"> <script src="${request.static_url('deform:static/' + reqt)}" type="text/javascript"></script> </tal:block> </head> <body> <div> <h1><img src="${request.static_url('tutorial:static/logo.png')}" alt="Logo"/>${title}</h1> <p tal:content="structure form.render()"> </p> </div> </body> </html> Friday, February 8, 13
  44. 44. 08: __init__.py from pyramid.config import Configurator def main(global_config, **settings): config = Configurator(settings=settings) config.add_route('hello', '/') config.add_static_view(name='static', path='tutorial:static') config.add_static_view('deform_static', 'deform:static/') config.scan() return config.make_wsgi_app() Friday, February 8, 13
  45. 45. 09: FormValidation Friday, February 8, 13
  46. 46. 09: views.py import colander import deform from pyramid.decorator import reify from pyramid.view import view_config, view_defaults class Person(colander.MappingSchema): name = colander.SchemaNode(colander.String()) age = colander.SchemaNode(colander.Integer(), validator=colander.Range(0, 200)) @view_defaults(route_name='hello', renderer='templates/helloworld.pt') class HelloWorld(object): title = 'Hello World' status_message = None def __init__(self, request): self.request = request @reify def form(self): schema = Person() return deform.Form(schema, buttons=('submit',)) @reify def reqts(self): reqts = self.form.get_widget_resources() return dict( js_links=reqts.get('js', []), css_links=reqts.get('css', []) ) Friday, February 8, 13
  47. 47. 09: views.py @view_defaults(route_name='hello', renderer='templates/helloworld.pt') class HelloWorld(object): title = 'Hello World' status_message = None @view_config() def hello_world(self): return dict(form=self.form) @view_config(request_param='submit') def submit_handler(self): form = self.form controls = self.request.POST.items() try: appstruct = form.validate(controls) except deform.ValidationFailure as e: return dict(form=e) ## Process the valid form data self.status_message = 'Added person: ' + appstruct['name'] return dict(form=form, appstruct=appstruct) Friday, February 8, 13
  48. 48. 09: helloworld.pt <html> <head> <title>${view.title}</title> <link rel="stylesheet" href="${request.static_url('tutorial:static/hello.css')}"/> <tal:block repeat="reqt view.reqts['css_links']"> <link rel="stylesheet" type="text/css" href="${request.static_url('deform:static/' + reqt)}"/> </tal:block> <tal:block repeat="reqt view.reqts['js_links']"> <script src="${request.static_url('deform:static/' + reqt)}" type="text/javascript"></script> </tal:block> </head> <body> <div> <h1><img src="${request.static_url('tutorial:static/logo.png')}" alt="Logo"/>${view.title}</h1> <p tal:condition="view.status_message"> <em>${view.status_message}</em> </p> <p tal:content="structure form.render()"> </p> </div> </body> </html> Friday, February 8, 13
  49. 49. Validation Friday, February 8, 13
  50. 50. ...and more • Other template languages • SQLAlchemy (or others) for storage/ retrieval • Authentication and authorization • Sessions, events, i18n, resources and traversal, advanced configuration, ... • Substance D Friday, February 8, 13

×