Slide 1

Slide 1 text

Pyramid, le framework web extensible Georges Dubus @georgesdubus github.com/madjar compiletoi.net

Slide 2

Slide 2 text

Présentation du framework Framework web : répondre à des requêtes web par des pages web Minimaliste : ne s’occupe que du cœur de problème (mapping url/code, templates, sécurité, i18n, . . . ) Non opinionated (n’impose pas beaucoup de choix) Rapide Super documenté (si c’est pas documenté, ça existe pas) Super solide (si c’est pas testé, c’est cassé) Python 3

Slide 3

Slide 3 text

Exemple rapide @view_config(route_name=’hello’, renderer=’hello.jinja’) def hello_world(request): name = request.matchdict[’name’] return dict(name=name, message=awesome_message(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() remarques : renderer, scan

Slide 4

Slide 4 text

Deux manières de mapper des urls à du code URL Dispatch Requête /georges/photos/1 Route /{user}/photos/{id} Vue view_photo(request) request.matchdict = {’user’: ’georges’, ’id’: ’1’} Traversal Requête /georges/photos/1 Objet /georges -> /georges/photos -> /georges/photos/1 -> Vue view_photo(, request)

Slide 5

Slide 5 text

Traversal /georges/photos/1 → get_root()[’georges’][’photos’][’1’] class PhotoContainer: # ... def __getitem__(self, item): try: return query(Photo).filter_by(owner=self.owner, id=int(item)).one() except (NoResultFound, ValueError): raise KeyError(item) @view_config(context=Photo) def view_photo(context, request): # ... (mon latex a mangé l’indentation)

Slide 6

Slide 6 text

Authorization class Photo: @property def __acl__(self): return ([(Allow, self.owner.id, ’edit’), (Allow, self.owner.id, ’view’)] + [(Allow, f.id, ’view’) for f in self.owner.friends] + [(Deny, Everyone, ’access’)]) @view_config(context=Photo, permission=’view’) def view_photo(context, request): # ... @view_config(context=Photo, name=’edit’, permission=’edit’) def edit_photo(context, request): # ...

Slide 7

Slide 7 text

Comparaison avec Django (différentes philosophies) Django fait tout : ORM, formulaires, interface d’admin, . . . Pyramid fournit pas grand chose et permet de facilement rajouter ce qu’on veut, ou remplacer des bouts : pleins de langages de template (jinja, mako, chameleon, . . . ) n’importe quoi pour le modèle (sqlalchemy, mongodb, fichiers, requêtes à un webservice, . . . ) librairies de validation de formulaires (avec ou sans génération du html) Django fournit plus de rails, ce qui rend la création d’applications pluggables plus simple. Les addons de pyramid sont plutôt là pour rajouter des fonctionnalités au framework.

Slide 8

Slide 8 text

Extensibilité Un mot sur extensibilité, parce ce que c’est là que pyramid brille vraiment. J’ai utilisé ça pour implémenter pyramid_persona. [INSERT SHAMELESS PLUG HERE]

Slide 9

Slide 9 text

Point d’entrée Un framework propose des points d’entrée, des manières de plugguer des choses. config = Configurator() authn_policy = AuthTktAuthenticationPolicy(’seekrit’) authz_policy = ACLAuthorizationPolicy() config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) config.add_request_method(get_user, ’user’, reify=True) config.add_subscriber(add_global, BeforeRender) config.set_default_permission(’access’) On appelle ça des directives de configuration. L’ordre n’a pas d’importance.

Slide 10

Slide 10 text

Vérification : validité config = Configurator() authz_policy = ACLAuthorizationPolicy() config.set_authorization_policy(authz_policy) config.make_wsgi_app() pyramid.exceptions.ConfigurationError : Cannot configure an authorization policy without also configuring an authentication policy (use the set_authorization_policy method)

Slide 11

Slide 11 text

Vérification : conflits config = Configurator() authn_policy1 = AuthTktAuthenticationPolicy(’seekrit’) authn_policy2 = SessionAuthenticationPolicy() config.set_authentication_policy(authn_policy1) config.set_authentication_policy(authn_policy2) config.make_wsgi_app() ConfigurationConflictError

Slide 12

Slide 12 text

Include : composition et modularité def auth(config): authn_policy = AuthTktAuthenticationPolicy(’seekrit’) authz_policy = ACLAuthorizationPolicy() config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) config = Configurator() authn_policy = SessionAuthenticationPolicy() config.set_authentication_policy(authn_policy) config.include(auth) Peuvent être imbriqués. Gestion des conflits avancé (ici, SessionAuthenticationPolicy écrase AuthTktAuthenticationPolicy). Pour faire des libs ou juste modulariser son code.

Slide 13

Slide 13 text

Ajout de directives def set_site_name(config, site_name): # ... config.add_directive(’set_site_name’, set_site_name) config.set_site_name(’foo’) Gestion des conflits sur ces nouvelles directives. Et aussi sur add_directive.

Slide 14

Slide 14 text

Écrire une lib Du coup, écrire une lib, c’est ça : (morceau de pyramid_persona) def includeme(config): authz_policy = ACLAuthorizationPolicy() config.set_authorization_policy(authz_policy) secret = settings.get(’persona.secret’, None) authn_policy = AuthTktAuthenticationPolicy(secret, hashalg=’sha512’) config.set_authentication_policy(authn_policy) # ... config.add_route(login_route, login_path) config.add_view(login, route_name=login_route, check_csrf=True, renderer=’json’, permission=NO_PERMISSION_REQUIRED) # ... config.add_forbidden_view(forbidden) config.add_request_method(button, ’persona_button’, reify=True)

Slide 15

Slide 15 text

Utiliser une lib Et l’utiliser, c’est ça : config.include(’pyramid_persona’) et éventuellement config.set_authorization_policy(another_authz_policy) config.add_view(another_login_view, route_name=’login’)

Slide 16

Slide 16 text

Pendant ce temps, dans django L’utilisateur de la librairie s’occupe de chaque point d’entrée à la main To use django-browserid, you’ll need to make a few changes to your settings.py file : # Add ’django_browserid’ to INSTALLED_APPS. INSTALLED_APPS = ( # ... ’django.contrib.auth’, ’django_browserid’, # Load after auth # ... ) # Add the django_browserid authentication backend. AUTHENTICATION_BACKENDS = ( # ... ’django_browserid.auth.BrowserIDBackend’, # ... ) # Add the django_browserid context processor. TEMPLATE_CONTEXT_PROCESSORS = ( # ... ’django_browserid.context_processors.browserid’, # ... ) Next, edit your urls.py file and add the following : urlpatterns = patterns(’’, # ... (r’^browserid/’, include(’django_browserid.urls’)), # ... )

Slide 17

Slide 17 text

Conclusion Pyramid est un framework très sympa avec un système d’extension très bien foutu. Il lui manque les rails, la base commune, pour avoir un écosystème comme celui de django, mais il est parfait pour construire d’autres framework (khufu, ptah, . . . ) qui, eux, fournissent des rails. En attendant, Pyramid est approprié pour les gens qui veulent un contrôle fin sur ce qu’ils font, ou qui font d’autres choix que ceux qu’impose Django.