Pyramid


Tips and tricks for building web apps

with this versatile framework


Roy Hyunjin Han


(Press the down arrow to move down)
(Press the right arrow to move right)

Pyramid is a framework for building web applications


pip install -U pyramid

Make your first app


pcreate -s starter whee
cd whee
python setup.py develop
pserve development.ini  # Note pyramid_debugtoolbar
vim whee/__init__.py

Learn more



Pyramid Documentation
http://docs.pylonsproject.org/projects/pyramid


Pyramid Cookbook
http://docs.pylonsproject.org/projects/pyramid_cookbook


Pyramid Updates
https://twitter.com/pylons

Views


vim whee/views.py

How the internet works


  1. Browser requests URL
  2. Server sends response

How views work


  1. Browser requests URL
  2. Route directs URL to view
  3. View transforms request into response
  4. Server sends response

# Check routes
proutes production.ini

# Check view corresponding to URL
pviews production.ini /

How to add a view


  1. Define your view
  2. Decorate it with view_config()

mkdir whee/views
touch whee/views/__init__.py
vim whee/views/yummies.py
@view_config(route_name='index', renderer='json')
def index(request):
    return dict(count=279)  # Great for testing!

How to add a view


  1. Define your view
  2. Decorate it with view_config()
  3. Add includeme() in view module
  4. Add config.add_route() in includeme()

vim whee/views/yummies.py
def includeme(config):
    config.scan(__name__)
    config.add_route('index', '/')

How to add a view


  1. Define your view
  2. Decorate it with view_config()
  3. Add includeme() in view module
  4. Add config.add_route() in includeme()
  5. Add config.include() in __init__.py

vim whee/__init__.py
def main(global_config, **settings):
    config = Configurator(settings=settings)
    config.include('whee.views.yummies')

Templates

vim whee/views.py
@view_config(route_name='home', renderer='templates/mytemplate.pt')
def my_view(request):
    return {'project': 'whee'}

Templates with Mako

  1. Point mako.directories to templates folder
vim development.ini
[app:main]
mako.directories = whee:templates

Templates with Mako

  1. Point mako.directories to templates folder
  2. Create template
vim whee/templates/index.mako
<!doctype html>
<html lang="en">
<button>${project.capitalize()}!</button>
</html>

Templates with Mako

  1. Point mako.directories to templates folder
  2. Create template
  3. Set renderer to template
vim whee/views.py
@view_config(route_name='home', renderer='index.mako')
def index(request):
    return {'project': 'whee'}

Renderer Globals

When rendering templates, it is useful to have 
global variables such as USER_ID or IS_MEMBER.
vim whee/__init__.py
from pyramid.events import BeforeRender
from pyramid.security import authenticated_userid

def main(global_config, **settings):
    def add_renderer_globals(event):
        request = event['request']
        userID = authenticated_userid(request)
        event.update(dict(
            USER_ID=userID))

    config = Configurator(settings=settings)
    config.add_subscriber(add_renderer_globals, BeforeRender)

AutoEscaping


Pyramid automatically escapes characters
when rendering templates
to prevent XSS attacks.







Thanks, MarkupSafe!

Databases

pcreate -s alchemy arrr
cd arrr
python setup.py develop
vim arrr/models.py

Transactions

If a view has an error,
the default transaction manager
pyramid_tm
undoes any database transactions.


Scripts on databases

vim arrr/scripts/initializedb.py

Scripts

pcreate
pserve
pshell
proutes
pviews
pcreate -s alchemy arrr
cd arrr
vim arrr/scripts/initializedb.py

How to add your own script

  1. Write script
vim arrr/scripts/update.py
import sys
from argparse import ArgumentParser
from pyramid.paster import get_appsettings
from sqlalchemy import engine_from_config
from ..models import DBSession

def main(argv=sys.argv):
    argumentParser = ArgumentParser()
    argumentParser.add_argument('configURL')
    arguments = argumentParser.parse_args(argv[1:])
    
    # Load settings
    settings = get_appsettings(arguments.configURL)
    print settings

    # Configure database
    engine = engine_from_config(settings, 'sqlalchemy.')
    DBSession.configure(bind=engine)

How to add your own script

  1. Write script
  2. Register it
vim setup.py
entry_points="""\
    [console_scripts]
    update_arrr = arrr.scripts.update:main""",
python setup.py develop
update_arrr development.ini

Ensure that only one instance of a script is running



Bind a socket
import socket
import sys


SOCKET = socket.socket()


if __name__ == '__main__':
    try:
        SOCKET.bind(('', 5000))
    except socket.error:
        sys.exit(1)

Debuggers

import pudb; pudb.set_trace()

import ipdb; ipdb.set_trace()  

import IPython; IPython.embed()

pshell development.ini

Permissions


  • Authenticate with your own database
  • Authenticate with a third-party provider

Permission concepts


  1. A user is in a group
  2. A group has permissions
  3. A permission gives access to selected views

Permission implementations


  1. A user is in a group, from get_groups()
  2. A group has permissions, from ACLs in RootFactory
  3. A permission gives access to selected views, from @view_config

How to authenticate with your own database

  1. Choose AuthorizationPolicy and AuthenticationPolicy
  2. Set default permission
  3. Define RootFactory ACLs for each permission and add to config
  4. Define get_groups and add to config
  5. Add permission to each view
  6. Incorporate get_groups() in add_renderer_globals() (optional)

How to authenticate with your own database

from pyramid.authentication import AuthTktAuthenticationPolicy
from pyramid.authorization import ACLAuthorizationPolicy
from pyramid.config import Configurator
from pyramid.security import Allow, Deny, Authenticated, ALL_PERMISSIONS


def main(global_config, **settings):

    def get_groups(userID, request):
        groups = []
        if userID == 'Tigger':
            groups.append('special')
        return groups

    authenticationPolicy = AuthTktAuthenticationPolicy(
        secret=settings['authtkt.secret'],
        callback=get_groups,
        hashalg='sha512')

    config = Configurator(
        authentication_policy=authenticationPolicy,
        authorization_policy=ACLAuthorizationPolicy(),
        default_permission='outside',
        root_factory='whee.RootFactory',
        settings=settings)
    return config.make_wsgi_app()


class RootFactory(object):
    __acl__ = [
        (Deny, 'outside', ALL_PERMISSIONS),
        (Allow, Authenticated, 'yummy_see'),
        (Allow, 'special', 'yummy_eat'),
    ]

    def __init__(self, request):
        pass

Authenticate with a 

third-party provider


pip install -U velruse

  1. Include velruse in config
  2. Specify third-party providers and credentials
  3. Define view for AuthenticationComplete
  4. Define view for AuthenticationDenied
  5. Add velruse.login_url() to your template

See velruse documentation

Servers


  • gevent
  • gevent-socketio
  • node.js

Serve with gevent

from gevent import wsgi, monkey; monkey.patch_all()
from pyramid.paster import get_app


class Proxy(object):

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        environ['wsgi.url_scheme'] = environ.get('HTTP_X_FORWARDED_PROTOCOL', 'http')
        return self.app(environ, start_response)


if '__main__' == __name__:
    app = get_app('production.ini')
    wsgi.WSGIServer(('0.0.0.0',w8000), Proxy(app)).serve_forever()

Serve with gevent-socketio

from socketio.server import SocketIOServer
from pyramid.paster import get_app
from gevent import monkey; monkey.patch_all()


if __name__ == '__main__':
    app = get_app('production.ini')
    SocketIOServer(('0.0.0.0', 8000), app).serve_forever()

Serve behind node.js

with SSL and websockets

npm install -g http-proxy
var https = require('https'),
    httpProxy = require('http-proxy'),
    fs = require('fs');
    
httpProxy.createServer(8888, 'localhost', {
    https: {
        key: fs.readFileSync('proxy.key', 'utf8'),
        cert: fs.readFileSync('proxy.pem', 'utf8')
    },
    target: {
        https: true
    }
}).listen(443);

Notes


  • Start URLs with double slashes if you are serving behind HTTPS
    <!--[if lt IE 9]>
        <script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    
  • Check the pyramid_cookbook

Caches

pip install -U dogpile.cache
from dogpile.cache import make_region

region = make_region().configure('dogpile.cache.memory')

@region.cache_on_arguments()
def compute(x, y):
    print '...',
    return x + y

print compute(1, 2)
print compute(1, 2)
compute.invalidate(1, 2)
print compute(1, 2)
  • Supports different backends: memory, memcached, redis, files
  • Can also speed up Mako template rendering
  • Check http://dogpilecache.readthedocs.org

Websockets



Update clients in real-time through websockets,
e.g. for dashboards and chat applications.


pip install -U gevent-socketio

How to use websockets


  1. Use socket.io server
  2. Define view containing socketio_manage()
  3. Define websocket behavior in namespace
  4. Connect to websocket in template
  5. Add security with ACLs

Learn more


Other addons


  • Send mail with pyramid_mailer
  • Store variables in HTTP sessions with pyramid_beaker
  • Build forms with pyramid_deform or pyramid_simpleform
  • Validate forms with formencode

Futures

Unified payments


  • Stripe
  • Braintree
  • Google Wallet
  • Amazon Payments
  • Square
  • PayPal

How you can contribute


Answer questions

Update cookbook
Write a book

Submit pull requests
Write an addon

Thanks!




Made with Slides.com