pyramid framework
DESCRIPTION
The good, the bad and why it's greatTRANSCRIPT
![Page 1: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/1.jpg)
Pyramid FrameworkThe good, the bad and why it's great.
Anatoly Bubenkov@bubenkoffPaylogic Groningen Office2.09.2013
![Page 2: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/2.jpg)
What is pyramid?
● Pyramid is a general, open source, Python web application development framework.
● The first release of Pyramid (repoze.bfg) in July of 2008.
● At the end of 2010, repoze.bfg ->Pyramid.● And joined Pylons project.Pyramid was inspired by Zope, Pylons (version 1.0) and Django. So Pyramid gets best from several concepts, combining them into a unique web framework.
![Page 3: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/3.jpg)
Zope roots :)
● Many features of Pyramid trace their origins back to Zope.
● Like Zope applications, Pyramid applications can be easily extended
● The concepts of traversal and declarative security in Pyramid were pioneered first in Zope.
![Page 4: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/4.jpg)
What Makes Pyramid Unique
Good equally for small and big applications. Common case:● You start from small app using some
framework, everything is ok● App grows, you add features, and framework
is now not able to satisfy your needs● You end up rewriting app/parts of it using
different framework● Pyramid ‘is’ for both big and small apps.
Architected to build complex things from simple, which always work.
![Page 5: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/5.jpg)
I want minimal!
One-file app:from wsgiref.simple_server import make_serverfrom pyramid.config import Configuratorfrom pyramid.response import Response
def hello_world(request): return Response('Hello %(name)s!' % request.matchdict)
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()
![Page 6: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/6.jpg)
Decorator-based views
Possible, but not required, that you configure view as decorator for the view function:from pyramid.view import view_configfrom pyramid.response import Response
@view_config(route_name='fred')def fred_view(request): return Response('fred')
The huge difference with ‘other’ frameworks:decorator actually does NOT change the view function directly. This process is separated to a stage named configuration scan, allowing the override and customization of existing apps.
![Page 7: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/7.jpg)
Class-based and function-based viewsHere’s a view callable defined as a function:from pyramid.response import Responsefrom pyramid.view import view_config
@view_config(route_name='aview')def aview(request): return Response('one')
Here’s a few views defined as methods of a class instead:from pyramid.response import Responsefrom pyramid.view import view_config
class AView(object): def __init__(self, request): self.request = request
@view_config(route_name='view_one') def view_one(self): return Response('one')
@view_config(route_name='view_two') def view_two(self): return Response('two')
![Page 8: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/8.jpg)
Assets? Templates are also assets!
● Asset specifications are strings that contain both a Python package name and a file or directory name, e.g. MyPackage:static/index.html.
● An asset specification can refer to a template, a translation directory, or any other package-bound static resource.
● Because asset specifications are used heavily in Pyramid, they provided a way to allow users to override assets easily and in granular way.
![Page 9: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/9.jpg)
Templating: pluggable
● Pyramid has a structured API that allows for pluggability of “renderers”.
● Templating systems such as Mako, Genshi, Chameleon, and Jinja2 can be treated as renderers.
● Renderer bindings for all of these templating systems already exist for use in Pyramid.
● But if you’d rather use another, it’s not a big deal.
![Page 10: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/10.jpg)
Views should be declared as views
Why the view function should return the rendered stuff? It’s hard to cover it with tests. Much better to separate logic.For example, instead of:from pyramid.renderers import render_to_response
def myview(request): return render_to_response('myapp:templates/mytemplate.pt', {'a':1}, request=request)
You CAN do this:from pyramid.view import view_config
@view_config(renderer='myapp:templates/mytemplate.pt') def myview(request): return {'a':1}
![Page 11: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/11.jpg)
Event system
Pyramid provides (and uses itself internally) the powerful event system.It emits events during its request processing lifecycle. You can subscribe any number of listeners to these events. For example, to be notified of a new request, you can subscribe to the NewRequest event. To be notified that a template is about to be rendered, you can subscribe to the BeforeRender event, and so forth.
![Page 12: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/12.jpg)
Using event system
Imperativefrom pyramid.events import NewRequest
from subscribers import mysubscriber
# "config" below is assumed to be an instance of a# pyramid.config.Configurator object
config.add_subscriber(mysubscriber, NewRequest)
Decoratorfrom pyramid.events import NewRequestfrom pyramid.events import subscriber
@subscriber(NewRequest)def mysubscriber(event): event.request.foo = 1
The big difference here, is that events are used even in the core, request processing, so you can easily modify it during it’s life cycle!
![Page 13: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/13.jpg)
What about speed?
Answer: fast enough● Apache 2.2.14 was used. Just because it’s standard● Python 2.6.5 and mod_wsgi 2.8 (embedded mode) were used.
![Page 14: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/14.jpg)
No singletons
● Import of a Pyramid application needn’t have any “import-time side effects”.
● Now try to import and and work with django app without ‘settings’ :)
● Helps a lot deploying your system using an asynchronous server.
● You can even run multiple copies of a similar but not identically configured Pyramid application within the same Python process.
![Page 15: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/15.jpg)
View predicates and many views per route
● Pyramid allows you to associate more than one view per route.
● You can create a route with the pattern /items and when the route is matched, you can shuffle off the request to one view if the request method is GET, another view if the request method is POST
● A system known as “view predicates” allows for this.● Predicates: request_type, request_method,
request_param, match_param, and lot more, and CUSTOM ones
Simple example:config.add_view('my.package.GET_view', route_name='xhr_route', xhr=True, permission='view', request_method='GET')
![Page 16: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/16.jpg)
Transaction management
● stolen from Zope● it’s potentially bad to have explicit commits
because you can accidentally change model after commit
● no explicit commit (but it’s of course still up to you)
● allows you to synchronize commits between multiple databases, and allows you to do things like conditionally send email if a transaction commits, but otherwise keep quiet.
![Page 17: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/17.jpg)
Configuration: conflict detection and custom directives
● Pyramid’s configuration system keeps track of your configuration statements
● Identical, or ‘conflicting’ statements are complained● You can have your custom constraints for configuration
directives
from pyramid.config import Configurator
def add_protected_xhr_views(config, module): module = config.maybe_dotted(module) for method in ('GET', 'POST', 'HEAD'): view = getattr(module, 'xhr_%s_view' % method, None) if view is not None: config.add_view(view, route_name='xhr_route', xhr=True, permission='view', request_method=method)
config = Configurator()config.add_directive('add_protected_xhr_views', add_protected_xhr_views)
![Page 18: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/18.jpg)
Configuration: conflict detection and custom directives
Then instead of:from pyramid.config import Configurator
config = Configurator()config.add_route('xhr_route', '/xhr/{id}')config.add_view('my.package.GET_view', route_name='xhr_route', xhr=True, permission='view', request_method='GET')config.add_view('my.package.POST_view', route_name='xhr_route', xhr=True, permission='view', request_method='POST')config.add_view('my.package.HEAD_view', route_name='xhr_route', xhr=True, permission='view', request_method='HEAD')
you get this:config.add_route('xhr_route', '/xhr/{id}')config.add_protected_xhr_views('my.package')
![Page 19: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/19.jpg)
Configuration: extensibility
● All the configuration statements that can be performed in your “main” Pyramid application can also be performed by included packages including the addition of views, routes, subscribers, and even authentication and authorization policies.
● You can even extend or override an existing application by including another application’s configuration in your own, overriding or adding new views and routes to it.
from pyramid.config import Configurator
if __name__ == '__main__': config = Configurator() config.include('pyramid_jinja2') config.include('pyramid_exclog') config.include('some.other.guys.package', route_prefix='/someotherguy')
![Page 20: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/20.jpg)
Flexible authentication and authorization: the thing you always missed
● Pyramid includes a flexible, pluggable authentication and authorization system.
● Predefined Pyramid plugpoint to plug in your custom authentication and authorization code
● If you want to change these schemes later, you can just change it in one place rather than everywhere in your code.
![Page 21: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/21.jpg)
Declarative securityfrom pyramid.security import Everyonefrom pyramid.security import Allow
class Blog(object): __acl__ = [ (Allow, Everyone, 'view'), (Allow, 'group:editors', 'add'), (Allow, 'group:editors', 'edit'), ]
from pyramid.config import Configuratorfrom pyramid.authentication import AuthTktAuthenticationPolicyfrom pyramid.authorization import ACLAuthorizationPolicyauthn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512')authz_policy = ACLAuthorizationPolicy()config = Configurator()config.set_authentication_policy(authn_policy)config.set_authorization_policy(authz_policy)
from pyramid.view import view_configfrom resources import Blog
@view_config(context=Blog, name='add_entry.html', permission='add')def blog_entry_add_view(request): """ Add blog entry code goes here """ pass
![Page 22: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/22.jpg)
Traversal● Traversal is a concept stolen from Zope● It allows you to create a tree of resources, each of which can be addressed
by one or more URLs
from wsgiref.simple_server import make_serverfrom pyramid.config import Configuratorfrom pyramid.response import Response
class Resource(dict): pass
def get_root(request): return Resource({'a': Resource({'b': Resource({'c': Resource()})})})
def hello_world_of_resources(context, request): output = "Here's a resource and its children: %s" % context return Response(output)
if __name__ == '__main__': config = Configurator(root_factory=get_root) config.add_view(hello_world_of_resources, context=Resource) app = config.make_wsgi_app() server = make_server('0.0.0.0', 8080, app) server.serve_forever()
![Page 23: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/23.jpg)
View response adapters
To follow DRY principle, often you don’t want to repeat returning the Response object from the view
def aview(request): return "Hello world!"
from pyramid.config import Configuratorfrom pyramid.response import Response
def string_response_adapter(s): response = Response(s) response.content_type = 'text/html' return response
if __name__ == '__main__': config = Configurator() config.add_response_adapter(string_response_adapter, basestring)
![Page 24: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/24.jpg)
Programmatic Introspection
It’s good to have a well-defined mechanism to get back what was registered earlier: views, urls, etc
from pyramid.view import view_config from pyramid.response import Response
@view_config(route_name='bar') def show_current_route_pattern(request): introspector = request.registry.introspector route_name = request.matched_route.name route_intr = introspector.get('routes', route_name) return Response(str(route_intr['pattern']))
![Page 25: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/25.jpg)
So what is bad then?
● Probably nothing● People are worried by ‘complexity’● You should always understand what are you
doing● Traversal and security are not easy, but very
powerful● Pyramid is not a monster like Django, you
get nothing if you don’t want● Much less existing packages (but the quality
of existing is high)
![Page 26: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/26.jpg)
Thanks for your attention!
Questions?
![Page 27: Pyramid Framework](https://reader034.vdocuments.net/reader034/viewer/2022051210/54ba1d614a7959f0038b4694/html5/thumbnails/27.jpg)
Reference:
● http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/introduction.html
● http://blog.curiasolutions.com/the-great-web-framework-shootout/