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

Minimal and Full Stack Pyramid: Any Way You Want It

Minimal and Full Stack Pyramid: Any Way You Want It

Pyramid Web Framework can as minimal or as full stack as you want.

Goodwill

April 23, 2013
Tweet

Other Decks in Programming

Transcript

  1. Or

  2. Why Pyramid? There are many frameworks out there. Django Flask

    Ruby on Rails Other Stuff Bottle Web2py
  3. Python! - Generalized language (not just web) - Has established

    standards - Awesome Documentation - 30,000 packages - Polymorphic - Dynamic - Easy to write Named after Monty Python
  4. What does Pyramid offer? Pyramid has the power of other

    major Python frameworks. - Can be minimal - Can be full stack - Extremely Flexible - Full Python 3 support - Windows Support - SQLAlchemy, ZODB
  5. Minimal Pyramid App from wsgiref.simple_server import make_server from pyramid.config import

    Configurator from pyramid.response import Response def hello_world(request): return Response('Hello %s!' % request.matchdict['name']) if __name__ == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.add_view(hello_world, route_name='hello') app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever()
  6. What Just Happened? from wsgiref.simple_server import make_server from pyramid.config import

    Configurator from pyramid.response import Response Import basic WSGI server Import Pyramid Configurator Import Response
  7. What Just Happened? (cont ...) def hello_world(request): return Response('Hello %s'

    % request.matchdict['name']) Create a View, and return a Response request.matchdict is a simple dictionary
  8. What Just Happened? (cont...) if __name__ == '__main__': config =

    Configurator() config.add_route('hello', '/hello/{name}') config.add_view(hello_world, route_name='hello') Create Instance of Pyramid Configuration (no silly global settings) Add a route, name it 'hello' and give it a path {name} part of the path will be available in request. matchdict dictionary Map the 'hello_world' view to 'hello' route
  9. What Just Happened? (cont...) app = config.make_wsgi_app() server = make_server('0.0.0.0',

    8080, app) server.serve_forever() Make a WSGI app Create a server instance with the app ... and serve it
  10. So Minimal Pyramid App is 6 lines (the rest are

    imports or WSGI server) from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response('Hello %s!' % request.matchdict['name']) if __name__ == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.add_view(hello_world, route_name='hello') app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever()
  11. Good Design - No configuration globals - Everything is passed

    in - Explicitly called and named - Views are callables (methods or classes) - We steer clear of magic
  12. We got Syntactic Sugar from wsgiref.simple_server import make_server from pyramid.config

    import Configurator from pyramid.view import view_config @view_config(route_name='hello', renderer='string') def hello_world(request): return 'Hello %s!' % request.matchdict['name'] if __name__ == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.scan() app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever()
  13. That's true, but at expense of being explicit! from wsgiref.simple_server

    import make_server from pyramid.config import Configurator from pyramid.view import view_config @view_config(route_name='hello', renderer='string') def hello_world( request ): return 'Hello %s!' % request.matchdict['name if __name__ == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.scan() app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever() from flask import Flask from flask import request app = Flask(__name__) @app.route('/') def hello_world(name): return \ 'Hello %s!' % name if __name__ == '__main__': app.run()
  14. Request Import Magic In Flask, "importing request" gets you a

    request object specific to your context. from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.view import view_config @view_config(route_name='hello', renderer='string') def hello_world( request ): return 'Hello %s!' % \ request.matchdict['name'] from flask import Flask from flask import \ request In Pyramid, the request object is passed into the view.
  15. if __name__ == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.scan()

    app = config.make_wsgi_app() app = Flask(__name__) Configuration and WSGI In Flask, the app serves both as a basic configurator as well as the WSGI app. In Pyramid, the Configurator is explicit, extensible and handles only configuration. It does not really have anything to do with WSGI, but it has a handy method that creates a WSGI app IF you need it.
  16. @view_config(route_name='hello', renderer='string') def hello_world(request): return 'Hello %s!' % \ request.matchdict['name']

    if __name__ == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.scan() @app.route('/') def hello_world(name): return \ 'Hello %s!' % name if __name__ == '__main__': app.run() Routing and View Configuration In Flask, app.route does A LOT: registers a route, maps to the decorated view, names the route after decorated view In Pyramid all those actions are separate and explicit
  17. @view_config(route_name='hello', renderer='string') def hello_world(request): return 'Hello %s!' % \ request.matchdict['name']

    if __name__ == '__main__': config = Configurator() config.add_route('hello', /hello/{name}') config.scan() @app.route('/') def hello_world(name): return \ 'Hello %s!' % name if __name__ == '__main__': app.run() Routing and View Configuration ... In Pyramid config.scan() adds the "view_config" decorated view to the registry. You always have to specify route order manually since some routes may conflict.
  18. def hello_world(request): return 'Hello %s!' % \ request.matchdict['name'] if __name__

    == '__main__': config = Configurator() config.add_route('hello', '/hello/{name}') config.add_view(hello_world, route_name='hello') @app.route('/') def hello_world(name): return \ 'Hello %s!' % name if __name__ == '__main__': app.run() Routing and View Configuration 3 In Pyramid the more explicit way to register views and routes is "config.add_view". In that case you do not need "config.scan" In Flask, app.route is a shortcut for 'add_url_rule' and certain properties set on the view callable.
  19. server = make_server('0.0.0.0', 8080, app) server.serve_forever() if __name__ == '__main__':

    app.run() Running the WSGI Server In Flask, app.run() run a built Werkzeug WSGI server In Pyramid, the WSGI server is not built in. It does provide easy integration with wsgiref, cherrypy and waitress and the default scaffolds set it up for you in a pinch.
  20. And we are done ... So as you can see,

    Pyramid apps are minimal but a little more explicit. That makes it a more flexible without sacrificing readability. Awesome, right?
  21. So you like full stack frameworks with a SQL ORM

    and the whistles It's as easy as: $ pcreate -s alchemy myapp $ cd myapp $ python ./setup.py develop As you can see Pyramid uses standard Python Packaging
  22. What do you get out of the box? - SQLAlchemy

    <3 + DB Bootstrap + Transaction Control - Mako and Chameleon Templates - Declarative Configuration (.ini files) - Standard Python Unittest Framework
  23. What do you get out of the box? - Debug

    Toolbar - WSGI Server (Waitress) - Standard Python Packaging system - Debug and Helper Scripts
  24. Project Layout myapp/ ├── myapp/ ├── myapp.egg-info ├── myapp.sqlite ├──

    development.ini ├── production.ini ├── setup.py ├── setup.cfg ├── MANIFEST.in ├── README.txt └── CHANGES.txt Project Package Folder Development and Production Configuration Files Project Package Configuration (setuptools) and distribution Project Description and Changelog Top Folder Package Egg SQLite Database
  25. Project layout (continued) myapp/ └── myapp ├── __init__.py ├── models.py

    ├── scripts │ ├── initializedb.py │ └── __init__.py ├── static ├── templates │ └── mytemplate.pt ├── tests.py └── views.py Project App Pyramid Configuration SQLAlchemy ORM configuration Database Models Project Help Scripts (usually console scripts) Static Assets (JS, CSS) Templates Tests Views
  26. Configuration [app:main] use = egg:myapp pyramid.reload_templates = true pyramid.debug_authorization =

    false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.default_locale_name = en pyramid.includes = pyramid_debugtoolbar pyramid_tm
  27. Configuration (continued ...) sqlalchemy.url = sqlite:///%(here)s/myapp.sqlite # By default, toolbar

    only appears for clients from IP addresses # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 ### # wsgi server configuration ### [server:main] use = egg:waitress#main host = 0.0.0.0
  28. Minimal Pyramid SQLAlchemy App (minimal app + sqlalchemy) from pyramid.config

    import Configurator from sqlalchemy import engine_from_config from .models import DBSession, Base, def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) Base.metadata.bind = engine config = Configurator(settings=settings) config.add_static_view('static','static',cache_max_age=3600) config.add_route('home', '/') config.scan() return config.make_wsgi_app()
  29. Want to use Jinja2? $ pip install pyramid_jinja2 [app:main] use

    = egg:myapp pyramid.default_locale_name = en pyramid.includes = pyramid_jinja2 pyramid_debugtoolbar pyramid_tm jinja2.directories = myapp:templates https://pyramid_jinja2.readthedocs.org/
  30. Need Beaker for Session and Cache? $ pip install pyramid_beaker

    [app:main] use = egg:myapp pyramid.default_locale_name = en pyramid.includes = pyramid_beaker pyramid_debugtoolbar pyramid_tm https://pyramid_beaker.readthedocs.org/
  31. Need a form library? $ pip install pyramid_deform [app:main] use

    = egg:myapp pyramid.default_locale_name = en pyramid.includes = pyramid_defomr pyramid_debugtoolbar pyramid_tm https://pyramid_deform.readthedocs.org/
  32. Pylons Project Docs: http://docs.pylonsproject.org Daily Tips on Pyramid/Pylons Twitter: @pylons

    Mailing List: http://groups.google/pylons-discuss Presenters Twitter: @goodwillbits