Pyramid
Pyramid
Pyramid
CONTENTS
Front Matter
Copyright, Trademarks, and Attributions Attributions . . . . . . . . . . . . . . . Print Production . . . . . . . . . . . . . Contacting The Publisher . . . . . . . . HTML Version and Source Code . . . . Typographical Conventions Author Introduction Audience . . . . . . . . . . . Book Content . . . . . . . . . The Genesis of repoze.bfg The Genesis of Pyramid . . . . Thanks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
i
iii iv iv iv iv v vii vii viii viii ix ix
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
I
1
Narrative Documentation
Pyramid Introduction 1.1 What Makes Pyramid Unique . . . . . 1.1.1 Single-le applications . . . . 1.1.2 Decorator-based conguration 1.1.3 URL generation . . . . . . . . 1.1.4 Static le serving . . . . . . . 1.1.5 Debug Toolbar . . . . . . . . 1.1.6 Debugging settings . . . . . . 1.1.7 Add-ons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1
3 4 5 5 6 6 6 7 7
1.2 1.3 2
1.1.8 Class-based and function-based views . . 1.1.9 Asset specications . . . . . . . . . . . . 1.1.10 Extensible templating . . . . . . . . . . 1.1.11 Rendered views can return dictionaries . 1.1.12 Event system . . . . . . . . . . . . . . . 1.1.13 Built-in internationalization . . . . . . . 1.1.14 HTTP caching . . . . . . . . . . . . . . 1.1.15 Sessions . . . . . . . . . . . . . . . . . . 1.1.16 Speed . . . . . . . . . . . . . . . . . . . 1.1.17 Exception views . . . . . . . . . . . . . 1.1.18 No singletons . . . . . . . . . . . . . . . 1.1.19 View predicates and many views per route 1.1.20 Transaction management . . . . . . . . . 1.1.21 Conguration conict detection . . . . . 1.1.22 Conguration extensibility . . . . . . . . 1.1.23 Flexible authentication and authorization 1.1.24 Traversal . . . . . . . . . . . . . . . . . 1.1.25 Tweens . . . . . . . . . . . . . . . . . . 1.1.26 View response adapters . . . . . . . . . . 1.1.27 Global response object . . . . . . . . . 1.1.28 Automating repetitive conguration . . . 1.1.29 Programmatic Introspection . . . . . . . 1.1.30 Python 3 Compatibility . . . . . . . . . . 1.1.31 Testing . . . . . . . . . . . . . . . . . . 1.1.32 Support . . . . . . . . . . . . . . . . . . 1.1.33 Documentation . . . . . . . . . . . . . . What Is The Pylons Project? . . . . . . . . . . . Pyramid and Other Web Frameworks . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . .
7 8 9 9 10 10 10 11 11 11 12 12 12 13 13 14 14 14 15 17 17 18 19 19 19 19 20 20 23 23 23 25 25 26 27 27 28 28 29 29 30 30
Installing Pyramid 2.1 Before You Install . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.1 If You Dont Yet Have A Python Interpreter (UNIX) . . 2.1.2 If You Dont Yet Have A Python Interpreter (Windows) . 2.2 Installing Pyramid on a UNIX System . . . . . . . . . . . . . . 2.2.1 Installing Setuptools On Python 2 . . . . . . . . . . . . 2.2.2 Installing Distribute On Python 3 . . . . . . . . . . . . 2.2.3 Installing the virtualenv Package . . . . . . . . . . 2.2.4 Creating the Virtual Python Environment . . . . . . . . 2.2.5 Installing Pyramid Into the Virtual Python Environment 2.3 Installing Pyramid on a Windows System . . . . . . . . . . . . 2.3.1 Windows Using Python 2 . . . . . . . . . . . . . . . . . 2.3.2 Windows Using Python 3 . . . . . . . . . . . . . . . . . 2.4 What Gets Installed . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
Application Conguration 3.1 Imperative Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2 Declarative Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.3 Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Creating Your First Pyramid Application 4.1 Hello World . . . . . . . . . . . . . . 4.1.1 Imports . . . . . . . . . . . . 4.1.2 View Callable Declarations . . 4.1.3 Application Conguration . . 4.1.4 Congurator Construction . . 4.1.5 Adding Conguration . . . . 4.1.6 WSGI Application Creation . 4.1.7 WSGI Application Serving . . 4.1.8 Conclusion . . . . . . . . . . 4.2 References . . . . . . . . . . . . . . .
31 31 32 33 35 35 36 37 37 37 38 38 39 39 40 41 41 42 43 44 45 47 47 48 50 51 52 54 54 55 57 58 58 59 60 60 61 61 62 62 63
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
Creating a Pyramid Project 5.1 Scaffolds Included with Pyramid . . . . . . . . . . . . 5.2 Creating the Project . . . . . . . . . . . . . . . . . . . 5.3 Installing your Newly Created Project for Development 5.4 Running The Tests For Your Application . . . . . . . . 5.5 Running The Project Application . . . . . . . . . . . . 5.5.1 Reloading Code . . . . . . . . . . . . . . . . . 5.6 Viewing the Application . . . . . . . . . . . . . . . . 5.6.1 The Debug Toolbar . . . . . . . . . . . . . . . 5.7 The Project Structure . . . . . . . . . . . . . . . . . . 5.8 The MyProject Project . . . . . . . . . . . . . . . . 5.8.1 development.ini . . . . . . . . . . . . . 5.8.2 production.ini . . . . . . . . . . . . . . 5.8.3 MANIFEST.in . . . . . . . . . . . . . . . . 5.8.4 setup.py . . . . . . . . . . . . . . . . . . . 5.8.5 setup.cfg . . . . . . . . . . . . . . . . . . 5.9 The myproject Package . . . . . . . . . . . . . . . 5.9.1 __init__.py . . . . . . . . . . . . . . . . 5.9.2 views.py . . . . . . . . . . . . . . . . . . . 5.9.3 static . . . . . . . . . . . . . . . . . . . . 5.9.4 templates/mytemplate.pt . . . . . . . 5.9.5 tests.py . . . . . . . . . . . . . . . . . . . 5.10 Modifying Package Structure . . . . . . . . . . . . . . 5.11 Using the Interactive Shell . . . . . . . . . . . . . . . 5.12 What Is This pserve Thing . . . . . . . . . . . . . . 5.13 Using an Alternate WSGI Server . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . .
Startup 6.1 The Startup Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.2 Deployment Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Request Processing URL Dispatch 8.1 High-Level Operational Overview . . . . . . . . . . 8.2 Route Conguration . . . . . . . . . . . . . . . . . . 8.2.1 Conguring a Route to Match a View . . . . 8.2.2 Route Pattern Syntax . . . . . . . . . . . . . 8.2.3 Route Declaration Ordering . . . . . . . . . 8.2.4 Route Conguration Arguments . . . . . . . 8.3 Route Matching . . . . . . . . . . . . . . . . . . . . 8.3.1 The Matchdict . . . . . . . . . . . . . . . . 8.3.2 The Matched Route . . . . . . . . . . . . . . 8.4 Routing Examples . . . . . . . . . . . . . . . . . . . 8.4.1 Example 1 . . . . . . . . . . . . . . . . . . 8.4.2 Example 2 . . . . . . . . . . . . . . . . . . 8.4.3 Example 3 . . . . . . . . . . . . . . . . . . 8.5 Matching the Root URL . . . . . . . . . . . . . . . 8.6 Generating Route URLs . . . . . . . . . . . . . . . 8.7 Static Routes . . . . . . . . . . . . . . . . . . . . . 8.8 Redirecting to Slash-Appended Routes . . . . . . . . 8.9 Debugging Route Matching . . . . . . . . . . . . . . 8.10 Using a Route Prex to Compose Applications . . . 8.11 Custom Route Predicates . . . . . . . . . . . . . . . 8.12 Route Factories . . . . . . . . . . . . . . . . . . . . 8.13 Using Pyramid Security With URL Dispatch . . . . . 8.14 Route View Callable Registration and Lookup Details 8.15 References . . . . . . . . . . . . . . . . . . . . . . .
65 65 68 69 75 75 75 76 77 81 82 82 83 83 83 83 84 85 86 86 88 88 90 91 92 95 96 97 98 99 99 100 100 101 101 102 103 103 105 105 107
7 8
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
Views 9.1 View Callables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.2 Dening a View Callable as a Function . . . . . . . . . . . . . . . . . . . . . . . . 9.3 Dening a View Callable as a Class . . . . . . . . . . . . . . . . . . . . . . . . . 9.4 View Callable Responses . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.5 Using Special Exceptions In View Callables . . . . . . . . . . . . . . . . . . . . . 9.5.1 HTTP Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.5.2 How Pyramid Uses HTTP Exceptions . . . . . . . . . . . . . . . . . . . . 9.6 Custom Exception Views . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9.7 Using a View Callable to Do an HTTP Redirect . . . . . . . . . . . . . . . . . . . 9.8 Handling Form Submissions in View Callables (Unicode and Character Set Issues) 9.9 Alternate View Callable Argument/Calling Conventions . . . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
9.10 Passing Conguration Variables to a View . . . . . . . . . . . . . . . . . . . . . . . . . 109 9.11 Pylons-1.0-Style Controller Dispatch . . . . . . . . . . . . . . . . . . . . . . . . . . 109 10 Renderers 10.1 Writing View Callables Which Use a Renderer . . . . . . . . . . 10.2 Built-In Renderers . . . . . . . . . . . . . . . . . . . . . . . . . 10.2.1 string: String Renderer . . . . . . . . . . . . . . . . . 10.2.2 JSON Renderer . . . . . . . . . . . . . . . . . . . . . . . 10.2.3 JSONP Renderer . . . . . . . . . . . . . . . . . . . . . . 10.2.4 *.pt or *.txt: Chameleon Template Renderers . . . . 10.2.5 *.mak or *.mako: Mako Template Renderer . . . . . . 10.3 Varying Attributes of Rendered Responses . . . . . . . . . . . . . 10.4 Deprecated Mechanism to Vary Attributes of Rendered Responses 10.5 Adding and Changing Renderers . . . . . . . . . . . . . . . . . . 10.5.1 Adding a New Renderer . . . . . . . . . . . . . . . . . . 10.5.2 Changing an Existing Renderer . . . . . . . . . . . . . . 10.6 Overriding A Renderer At Runtime . . . . . . . . . . . . . . . . 111 112 113 113 114 116 117 118 119 120 121 121 124 124 127 127 130 131 133 134 134 135 136 137 137 138 138 138 139 139 140 141 141 142 149 152 153 156 156
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
11 Templates 11.1 Using Templates Directly . . . . . . . . . . . . . . . . . . . . . . . 11.2 System Values Used During Rendering . . . . . . . . . . . . . . . . 11.3 Templates Used as Renderers via Conguration . . . . . . . . . . . 11.4 Chameleon ZPT Templates . . . . . . . . . . . . . . . . . . . . . . 11.4.1 A Sample ZPT Template . . . . . . . . . . . . . . . . . . . 11.4.2 Using ZPT Macros in Pyramid . . . . . . . . . . . . . . . . 11.4.3 Using A Chameleon Macro Name Within a Renderer Name 11.5 Templating with Chameleon Text Templates . . . . . . . . . . . . . 11.6 Side Effects of Rendering a Chameleon Template . . . . . . . . . . 11.7 Debugging Templates . . . . . . . . . . . . . . . . . . . . . . . . . 11.8 Chameleon Template Internationalization . . . . . . . . . . . . . . 11.9 Templating With Mako Templates . . . . . . . . . . . . . . . . . . 11.9.1 A Sample Mako Template . . . . . . . . . . . . . . . . . . 11.9.2 Using A Mako def name Within a Renderer Name . . . . . 11.10Automatically Reloading Templates . . . . . . . . . . . . . . . . . 11.11Available Add-On Template System Bindings . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . .
12 View Conguration 12.1 Mapping a Resource or URL Pattern to a View Callable . . . . . . . . . . . 12.1.1 View Conguration Parameters . . . . . . . . . . . . . . . . . . . 12.1.2 Adding View Conguration Using the @view_config Decorator 12.1.3 Adding View Conguration Using add_view() . . . . . . . . . 12.2 @view_defaults Class Decorator . . . . . . . . . . . . . . . . . . . . 12.2.1 Conguring View Security . . . . . . . . . . . . . . . . . . . . . . 12.2.2 NotFound Errors . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
12.3 Inuencing HTTP Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 12.4 Debugging View Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 13 Static Assets 13.1 Understanding Asset Specications . . . . . . . . . . . . . . . 13.2 Serving Static Assets . . . . . . . . . . . . . . . . . . . . . . . 13.2.1 Generating Static Asset URLs . . . . . . . . . . . . . . 13.3 Advanced: Serving Static Assets Using a View Callable . . . . . 13.3.1 Root-Relative Custom Static View (URL Dispatch Only) 13.3.2 Registering A View Callable to Serve a Static Asset . 13.4 Overriding Assets . . . . . . . . . . . . . . . . . . . . . . . . . 13.4.1 The override_asset API . . . . . . . . . . . . . . 14 Request and Response Objects 14.1 Request . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.1.1 Special Attributes Added to the Request by Pyramid 14.1.2 URLs . . . . . . . . . . . . . . . . . . . . . . . . . 14.1.3 Methods . . . . . . . . . . . . . . . . . . . . . . . 14.1.4 Unicode . . . . . . . . . . . . . . . . . . . . . . . . 14.1.5 Multidict . . . . . . . . . . . . . . . . . . . . . . . 14.1.6 Dealing With A JSON-Encoded Request Body . . . 14.1.7 Cleaning Up After a Request . . . . . . . . . . . . . 14.1.8 More Details . . . . . . . . . . . . . . . . . . . . . 14.2 Response . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.1 Headers . . . . . . . . . . . . . . . . . . . . . . . . 14.2.2 Instantiating the Response . . . . . . . . . . . . . . 14.2.3 Exception Responses . . . . . . . . . . . . . . . . . 14.2.4 More Details . . . . . . . . . . . . . . . . . . . . . 15 Sessions 15.1 Using The Default Session Factory . . . . . . . . . . . . . 15.2 Using a Session Object . . . . . . . . . . . . . . . . . . . 15.3 Using Alternate Session Factories . . . . . . . . . . . . . 15.4 Creating Your Own Session Factory . . . . . . . . . . . . 15.5 Flash Messages . . . . . . . . . . . . . . . . . . . . . . . 15.5.1 Using the session.flash Method . . . . . . . 15.5.2 Using the session.pop_flash Method . . . . 15.5.3 Using the session.peek_flash Method . . . 15.6 Preventing Cross-Site Request Forgery Attacks . . . . . . 15.6.1 Using the session.get_csrf_token Method 15.6.2 Using the session.new_csrf_token Method 159 159 160 162 163 164 165 165 166 169 170 171 171 171 172 172 172 174 174 175 176 176 176 176 177 177 178 179 180 180 180 181 181 182 182 183
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
16.2 Conguring an Event Listener Using a Decorator . . . . . . . . . . . . . . . . . . . . . 186 16.3 An Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 17 Environment Variables and .ini File Settings 189 17.1 Reloading Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 17.2 Reloading Assets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 17.3 Debugging Authorization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 17.4 Debugging Not Found Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 17.5 Debugging Route Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 17.6 Preventing HTTP Caching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 17.7 Debugging All . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 17.8 Reloading All . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 17.9 Default Locale Name . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 17.10Including Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 17.10.1 pyramid.includes vs. pyramid.config.Configurator.include() 192 17.11Explicit Tween Conguration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 17.11.1 PasteDeploy Conguration vs. Plain-Python Conguration . . . . . . . . . . . . 194 17.12Mako Template Render Settings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 17.12.1 Mako Directories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 17.12.2 Mako Module Directory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 17.12.3 Mako Input Encoding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 17.12.4 Mako Error Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 17.12.5 Mako Default Filters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 17.12.6 Mako Import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 17.12.7 Mako Strict Undened . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 17.12.8 Mako Preprocessor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 17.13Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 17.14Understanding the Distinction Between reload_templates and reload_assets 198 17.15Adding A Custom Setting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 18 Logging 18.1 Logging Conguration . . . . . . . . . . . 18.2 Sending Logging Messages . . . . . . . . . 18.3 Filtering log messages . . . . . . . . . . . 18.4 Advanced Conguration . . . . . . . . . . 18.5 Logging Exceptions . . . . . . . . . . . . . 18.6 Request Logging with Pastes TransLogger 201 201 204 205 206 206 206 209 209 210 212 213
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
19 PasteDeploy Conguration Files 19.1 PasteDeploy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19.1.1 Entry Points and PasteDeploy .ini Files . . . . . . . . . . . . . . . . . . . . . 19.1.2 [DEFAULTS] Section of a PasteDeploy .ini File . . . . . . . . . . . . . . . 20 Command-Line Pyramid
20.1 Displaying Matching Views for a Given URL 20.2 The Interactive Shell . . . . . . . . . . . . . 20.2.1 Extending the Shell . . . . . . . . . . 20.2.2 IPython or bpython . . . . . . . . . . 20.3 Displaying All Application Routes . . . . . . 20.4 Displaying Tweens . . . . . . . . . . . . . 20.5 Invoking a Request . . . . . . . . . . . . . . 20.6 Writing a Script . . . . . . . . . . . . . . . . 20.6.1 Changing the Request . . . . . . . . 20.6.2 Cleanup . . . . . . . . . . . . . . . . 20.6.3 Setting Up Logging . . . . . . . . . . 20.7 Making Your Script into a Console Script . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
213 215 216 218 218 219 220 221 224 224 225 225 231 231 231 233 234 235 236 238 239 239 239 240 240 241 242 242 243 243 244 245 245 246 246 246 247 249 249 250 251
21 Internationalization and Localization 21.1 Creating a Translation String . . . . . . . . . . . . . . . . . 21.1.1 Using The TranslationString Class . . . . . 21.1.2 Using the TranslationStringFactory Class 21.2 Working With gettext Translation Files . . . . . . . . . . 21.2.1 Installing Babel and Lingua . . . . . . . . . . . . . 21.2.2 Extracting Messages from Code and Templates . . . 21.2.3 Initializing a Message Catalog File . . . . . . . . . . 21.2.4 Updating a Catalog File . . . . . . . . . . . . . . . 21.2.5 Compiling a Message Catalog File . . . . . . . . . . 21.3 Using a Localizer . . . . . . . . . . . . . . . . . . . . . . . 21.3.1 Performing a Translation . . . . . . . . . . . . . . . 21.3.2 Performing a Pluralization . . . . . . . . . . . . . . 21.4 Obtaining the Locale Name for a Request . . . . . . . . . . 21.5 Performing Date Formatting and Currency Formatting . . . 21.6 Chameleon Template Support for Translation Strings . . . . 21.7 Mako Pyramid I18N Support . . . . . . . . . . . . . . . . . 21.8 Localization-Related Deployment Settings . . . . . . . . . . 21.9 Detecting Available Languages . . . . . . . . . . . . . . . 21.10Activating Translation . . . . . . . . . . . . . . . . . . . . . 21.10.1 Adding a Translation Directory . . . . . . . . . . . 21.10.2 Setting the Locale . . . . . . . . . . . . . . . . . . 21.11Locale Negotiators . . . . . . . . . . . . . . . . . . . . . . 21.11.1 The Default Locale Negotiator . . . . . . . . . . . . 21.11.2 Using a Custom Locale Negotiator . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . .
22 Virtual Hosting 22.1 Hosting an Application Under a URL Prex . . . . . . . . . . . . . . . . . . . . . . . . 22.2 Virtual Root Support . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22.3 Further Documentation and Examples . . . . . . . . . . . . . . . . . . . . . . . . . . .
23 Unit, Integration, and Functional Testing 23.1 Test Set Up and Tear Down . . . . . . . . . . . . . . . . . . . . . . . . . 23.1.1 Test setup using a context manager . . . . . . . . . . . . . . . . . 23.1.2 What? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23.2 Using the Configurator and pyramid.testing APIs in Unit Tests 23.3 Creating Integration Tests . . . . . . . . . . . . . . . . . . . . . . . . . . 23.4 Creating Functional Tests . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Resources 24.1 Dening a Resource Tree . . . . . . . . . . . . . . . . . . . . . . 24.2 Location-Aware Resources . . . . . . . . . . . . . . . . . . . . . 24.3 Generating The URL Of A Resource . . . . . . . . . . . . . . . . 24.3.1 Overriding Resource URL Generation . . . . . . . . . . . 24.4 Generating the Path To a Resource . . . . . . . . . . . . . . . . . 24.5 Finding a Resource by Path . . . . . . . . . . . . . . . . . . . . . 24.6 Obtaining the Lineage of a Resource . . . . . . . . . . . . . . . . 24.7 Determining if a Resource is In The Lineage of Another Resource 24.8 Finding the Root Resource . . . . . . . . . . . . . . . . . . . . . 24.9 Resources Which Implement Interfaces . . . . . . . . . . . . . . 24.10Finding a Resource With a Class or Interface in Lineage . . . . . 24.11Pyramid API Functions That Act Against Resources . . . . . . . . 25 Much Ado About Traversal 25.1 URL Dispatch . . . . . . . . . . . 25.2 Historical Refresher . . . . . . . . 25.3 Traversal (aka Resource Location) 25.4 What Is a Resource? . . . . . . 25.5 View Lookup . . . . . . . . . . . 25.6 Use Cases . . . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
253 254 256 256 256 259 260 261 262 263 264 265 266 267 268 268 269 269 271 272 273 274 274 275 276 277 278 281 281 283 285 285 288 290 292 293 294 294 295 295
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . . . . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
. . . . . .
26 Traversal 26.1 Traversal Details . . . . . . . . . . . . . . . . . . . . . 26.2 The Resource Tree . . . . . . . . . . . . . . . . . . . . 26.3 The Traversal Algorithm . . . . . . . . . . . . . . . . . 26.3.1 A Description of The Traversal Algorithm . . . . 26.3.2 Traversal Algorithm Examples . . . . . . . . . . 26.3.3 Using Resource Interfaces In View Conguration 26.4 References . . . . . . . . . . . . . . . . . . . . . . . . . 27 Security 27.1 Enabling an Authorization Policy . . . . . . . . . . . . 27.1.1 Enabling an Authorization Policy Imperatively 27.2 Protecting Views with Permissions . . . . . . . . . . . 27.2.1 Setting a Default Permission . . . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
27.3 Assigning ACLs to your Resource Objects . . 27.4 Elements of an ACL . . . . . . . . . . . . . 27.5 Special Principal Names . . . . . . . . . . . 27.6 Special Permissions . . . . . . . . . . . . . . 27.7 Special ACEs . . . . . . . . . . . . . . . . . 27.8 ACL Inheritance and Location-Awareness . . 27.9 Changing the Forbidden View . . . . . . . . 27.10Debugging View Authorization Failures . . . 27.11Debugging Imperative Authorization Failures 27.12Creating Your Own Authentication Policy . . 27.13Creating Your Own Authorization Policy . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
. . . . . . . . . . .
296 297 299 299 300 300 301 301 301 302 303 305 305 305 306 306 308 308 311 312 313 313 314 315 321 321 323 325 325 327 328 328 329 331 332 334 335 337 337 339
28 Combining Traversal and URL Dispatch 28.1 A Review of Non-Hybrid Applications . . . . . . . . . . . . . . . . . . . . . . 28.1.1 URL Dispatch Only . . . . . . . . . . . . . . . . . . . . . . . . . . . 28.1.2 Traversal Only . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28.2 Hybrid Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28.2.1 The Root Object for a Route Match . . . . . . . . . . . . . . . . . . . 28.2.2 Using *traverse In a Route Pattern . . . . . . . . . . . . . . . . . 28.2.3 Using the traverse Argument In a Route Denition . . . . . . . . . 28.2.4 Using *subpath in a Route Pattern . . . . . . . . . . . . . . . . . . 28.3 Corner Cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28.3.1 Registering a Default View for a Route That Has a view Attribute . . . 28.3.2 Binding Extra Views Against a Route Conguration that Doesnt *traverse Element In Its Pattern . . . . . . . . . . . . . . . . . . . 29 Invoking a Subrequest
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Have a . . . . .
30 Using Hooks 30.1 Changing the Not Found View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.2 Changing the Forbidden View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.3 Changing the Request Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.4 Using The Before Render Event . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.5 Adding Renderer Globals (Deprecated) . . . . . . . . . . . . . . . . . . . . . . . . . . 30.6 Using Response Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.7 Using Finished Callbacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.8 Changing the Traverser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.9 Changing How pyramid.request.Request.resource_url() Generates a URL 30.10Changing How Pyramid Treats View Responses . . . . . . . . . . . . . . . . . . . . . . 30.11Using a View Mapper . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.12Registering Conguration Decorators . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.13Registering Tweens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.13.1 Creating a Tween Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30.13.2 Registering an Implicit Tween Factory . . . . . . . . . . . . . . . . . . . . . . .
30.13.3 Suggesting Implicit Tween Ordering . . . . . . . . 30.13.4 Explicit Tween Ordering . . . . . . . . . . . . . . 30.13.5 Tween Conicts and Ordering Cycles . . . . . . . 30.13.6 Displaying Tween Ordering . . . . . . . . . . . . 30.14Adding A Third Party View, Route, or Subscriber Predicate 30.14.1 View and Route Predicates . . . . . . . . . . . . . 30.14.2 Subscriber Predicates . . . . . . . . . . . . . . . . 31 Pyramid Conguration Introspection 31.1 Using the Introspector . . . . . . . 31.2 Introspectable Objects . . . . . . 31.3 Pyramid Introspection Categories . 31.4 Introspection in the Toolbar . . . . 31.5 Disabling Introspection . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
340 341 342 343 343 343 345 347 347 348 349 360 360 361 361 362 363 363 363 364 365 366 366 367 367 369 372 372 372 373 374 375 375 376 379 380
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
32 Extending An Existing Pyramid Application 32.1 The Difference Between Extensible and Pluggable Applications 32.2 Rules for Building An Extensible Application . . . . . . . . . . . . 32.2.1 Fundamental Plugpoints . . . . . . . . . . . . . . . . . . . 32.3 Extending an Existing Application . . . . . . . . . . . . . . . . . . 32.3.1 If The Application Has Conguration Decorations . . . . . 32.3.2 Extending the Application . . . . . . . . . . . . . . . . . . 32.3.3 Overriding Views . . . . . . . . . . . . . . . . . . . . . . . 32.3.4 Overriding Routes . . . . . . . . . . . . . . . . . . . . . . 32.3.5 Overriding Assets . . . . . . . . . . . . . . . . . . . . . . . 33 Advanced Conguration 33.1 Conict Detection . . . . . . . . . . . . . . . . . . 33.1.1 Manually Resolving Conicts . . . . . . . 33.1.2 Automatic Conict Resolution . . . . . . . 33.1.3 Methods Which Provide Conict Detection 33.2 Including Conguration from External Sources . . 33.3 Two-Phase Conguration . . . . . . . . . . . . . . 33.4 More Information . . . . . . . . . . . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
. . . . . . .
34 Extending Pyramid Conguration 34.1 Adding Methods to the Congurator via add_directive 34.2 Using config.action in a Directive . . . . . . . . . . . 34.3 Adding Conguration Introspection . . . . . . . . . . . . . 34.3.1 Introspectable Relationships . . . . . . . . . . . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
35 Thread Locals 383 35.1 Why and How Pyramid Uses Thread Local Variables . . . . . . . . . . . . . . . . . . . 383 35.2 Why You Shouldnt Abuse Thread Locals . . . . . . . . . . . . . . . . . . . . . . . . . 384
36 Using the Zope Component Architecture in Pyramid 36.1 Using the ZCA Global API in a Pyramid Application . . . . . . . . . . . . 36.1.1 Disusing the Global ZCA API . . . . . . . . . . . . . . . . . . . . 36.1.2 Enabling the ZCA Global API by Using hook_zca . . . . . . . . 36.1.3 Enabling the ZCA Global API by Using The ZCA Global Registry .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
II
Tutorials
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
393
395 395 396 396 396 396 397 397 399 399 399 400 400 401 401 402 402 402 403 404 405 406 408 408 408 409 410 410 411 412 415 416 420 421 422
37 ZODB + Traversal Wiki Tutorial 37.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . 37.2 Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37.2.1 Overall . . . . . . . . . . . . . . . . . . . . . . . 37.2.2 Models . . . . . . . . . . . . . . . . . . . . . . . 37.2.3 Views . . . . . . . . . . . . . . . . . . . . . . . . 37.2.4 Security . . . . . . . . . . . . . . . . . . . . . . . 37.2.5 Summary . . . . . . . . . . . . . . . . . . . . . . 37.3 Installation . . . . . . . . . . . . . . . . . . . . . . . . . 37.3.1 Preparation . . . . . . . . . . . . . . . . . . . . . 37.3.2 Make a Project . . . . . . . . . . . . . . . . . . . 37.3.3 Install the Project in Development Mode . . . . 37.3.4 Run the Tests . . . . . . . . . . . . . . . . . . . . 37.3.5 Expose Test Coverage Information . . . . . . . . . 37.3.6 Start the Application . . . . . . . . . . . . . . . . 37.3.7 Visit the Application in a Browser . . . . . . . . . 37.3.8 Decisions the zodb Scaffold Has Made For You . 37.4 Basic Layout . . . . . . . . . . . . . . . . . . . . . . . . 37.4.1 Application Conguration with __init__.py . 37.4.2 Resources and Models with models.py . . . . . 37.4.3 Views With views.py . . . . . . . . . . . . . . 37.4.4 Conguration in development.ini . . . . . . 37.5 Dening the Domain Model . . . . . . . . . . . . . . . . 37.5.1 Delete the Database . . . . . . . . . . . . . . . . . 37.5.2 Edit models.py . . . . . . . . . . . . . . . . . 37.5.3 Look at the Result of Our Edits to models.py . . 37.5.4 View the Application in a Browser . . . . . . . . . 37.6 Dening Views . . . . . . . . . . . . . . . . . . . . . . . 37.6.1 Declaring Dependencies in Our setup.py File . 37.6.2 Adding View Functions . . . . . . . . . . . . . . 37.6.3 Viewing the Result of all Our Edits to views.py 37.6.4 Adding Templates . . . . . . . . . . . . . . . . . 37.6.5 Viewing the Application in a Browser . . . . . . . 37.7 Adding Authorization . . . . . . . . . . . . . . . . . . . . 37.7.1 Access Control . . . . . . . . . . . . . . . . . . .
37.7.2 Login, Logout . . . . . . . . . . . . . . . . . 37.7.3 Seeing Our Changes . . . . . . . . . . . . . . 37.7.4 Viewing the Application in a Browser . . . . . 37.8 Adding Tests . . . . . . . . . . . . . . . . . . . . . . 37.8.1 Test the Models . . . . . . . . . . . . . . . . . 37.8.2 Test the Views . . . . . . . . . . . . . . . . . 37.8.3 Functional tests . . . . . . . . . . . . . . . . . 37.8.4 View the results of all our edits to tests.py 37.8.5 Running the Tests . . . . . . . . . . . . . . . . 37.9 Distributing Your Application . . . . . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
. . . . . . . . . .
425 429 436 437 437 437 437 437 442 444 445 445 446 446 446 446 447 447 449 449 449 450 450 451 452 453 454 454 455 457 459 461 461 463 464 465 465 466 467 468 473 477
38 SQLAlchemy + URL Dispatch Wiki Tutorial 38.1 Background . . . . . . . . . . . . . . . . . . . . . . . . . . 38.2 Design . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38.2.1 Overall . . . . . . . . . . . . . . . . . . . . . . . . 38.2.2 Models . . . . . . . . . . . . . . . . . . . . . . . . 38.2.3 Views . . . . . . . . . . . . . . . . . . . . . . . . . 38.2.4 Security . . . . . . . . . . . . . . . . . . . . . . . . 38.2.5 Summary . . . . . . . . . . . . . . . . . . . . . . . 38.3 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . 38.3.1 Preparation . . . . . . . . . . . . . . . . . . . . . . 38.3.2 Making a Project . . . . . . . . . . . . . . . . . . . 38.3.3 Installing the Project in Development Mode . . . . 38.3.4 Running the Tests . . . . . . . . . . . . . . . . . . . 38.3.5 Exposing Test Coverage Information . . . . . . . . 38.3.6 Initializing the Database . . . . . . . . . . . . . . . 38.3.7 Starting the Application . . . . . . . . . . . . . . . 38.3.8 Decisions the alchemy Scaffold Has Made For You 38.4 Basic Layout . . . . . . . . . . . . . . . . . . . . . . . . . 38.4.1 Application Conguration with __init__.py . . 38.4.2 View Declarations via views.py . . . . . . . . . . 38.4.3 Content Models with models.py . . . . . . . . . 38.5 Dening the Domain Model . . . . . . . . . . . . . . . . . 38.5.1 Making Edits to models.py . . . . . . . . . . . . 38.5.2 Changing scripts/initializedb.py . . . . 38.5.3 Reinitializing the Database . . . . . . . . . . . . . . 38.5.4 Viewing the Application in a Browser . . . . . . . . 38.6 Dening Views . . . . . . . . . . . . . . . . . . . . . . . . 38.6.1 Declaring Dependencies in Our setup.py File . . 38.6.2 Running setup.py develop . . . . . . . . . . 38.6.3 Changing the views.py File . . . . . . . . . . . . 38.6.4 Adding Templates . . . . . . . . . . . . . . . . . . 38.6.5 Adding Routes to __init__.py . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
38.6.6 Viewing the Application in a Browser . . . . . . 38.7 Adding Authorization . . . . . . . . . . . . . . . . . . . 38.7.1 Access Control . . . . . . . . . . . . . . . . . . 38.7.2 Login, Logout . . . . . . . . . . . . . . . . . . 38.7.3 Seeing Our Changes . . . . . . . . . . . . . . . 38.7.4 Viewing the Application in a Browser . . . . . . 38.8 Adding Tests . . . . . . . . . . . . . . . . . . . . . . . 38.8.1 Testing the Models . . . . . . . . . . . . . . . . 38.8.2 Testing the Views . . . . . . . . . . . . . . . . . 38.8.3 Functional tests . . . . . . . . . . . . . . . . . . 38.8.4 Viewing the results of all our edits to tests.py 38.8.5 Running the Tests . . . . . . . . . . . . . . . . . 38.9 Distributing Your Application . . . . . . . . . . . . . . 39 Converting a repoze.bfg Application to Pyramid 40 Running a Pyramid Application under mod_wsgi
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
. . . . . . . . . . . . .
478 479 480 483 487 495 495 496 496 496 496 502 503 505 509
III
API Reference
513
515
41 pyramid.authorization
42 pyramid.authentication 517 42.1 Authentication Policies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 517 42.2 Helper Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 526 43 pyramid.config 529
44 pyramid.events 573 44.1 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 573 44.2 Event Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 574 45 pyramid.exceptions 579
48 pyramid.interfaces 599 48.1 Event-Related Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599 48.2 Other Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 601 49 pyramid.location 617
54 pyramid.response 651 54.1 Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 657 55 pyramid.scripting 56 pyramid.security 56.1 Authentication API Functions 56.2 Authorization API Functions . 56.3 Constants . . . . . . . . . . . 56.4 Return Values . . . . . . . . . 57 pyramid.settings 58 pyramid.testing 59 pyramid.threadlocal 60 pyramid.traversal 61 pyramid.url 62 pyramid.view 63 pyramid.wsgi 659 661 661 662 663 664 665 667 671 673 679 681 685
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
IV
687
689
Glossary
Front Matter
While the Pyramid documentation is offered under the Creative Commons AttributionNonconmmercial-Share Alike 3.0 United States License, the Pyramid software is offered under a less restrictive (BSD-like) license .
All terms mentioned in this book that are known to be trademarks or service marks have been appropriately capitalized. However, use of a term in this book should not be regarded as affecting the validity of any trademark or service mark. Every effort has been made to make this book as complete and as accurate as possible, but no warranty or tness is implied. The information provided is on as as-is basis. The author and the publisher shall have neither liability nor responsibility to any person or entity with respect to any loss or damages arising from the information contained in this book. No patent liability is assumed with respect to the use of the information contained herein.
iii
Attributions
Editor: Casey Duncan Contributors: Ben Bangert, Blaise Laamme, Rob Miller, Mike Orr, Carlos de la Guardia, Paul Everitt, Tres Seaver, John Shipman, Marius Gedminas, Chris Rossi, Joachim Krebs, Xavier Spriet, Reed OBrien, William Chambers, Charlie Choiniere, Jamaludin Ahmad, Graham Higgins, Patricio Paez, Michael Merickel, Eric Ongerth, Niall OHiggins, Christoph Zwerschke, John Anderson, Atsushi Odagiri, Kirk Strauser, JD Navarro, Joe Dallago, Savoir-Faire Linux, ukasz Fidosz, Christopher Lambacher, Claus Conrad, Chris Beelby, Phil Jenvey and a number of people with only pseudonyms on GitHub. Cover Designer: Hugues Laamme of Kemeneur. Used with permission: The Request and Response Objects chapter is adapted, with permission, from documentation originally written by Ian Bicking. The Much Ado About Traversal chapter is adapted, with permission, from an article written by Rob Miller. The Logging is adapted, with permission, from the Pylons documentation logging chapter, originally written by Phil Jenvey.
Print Production
The print version of this book was produced using the Sphinx documentation generation system and the LaTeX typesetting system.
TYPOGRAPHICAL CONVENTIONS
Literals, lenames and function arguments are presented using the following style: argument1 Warnings, which represent limitations and need-to-know information related to a topic or concept are presented in the following style:
This is a warning. Notes, which represent additional information related to a topic or concept are presented in the following style:
This is a note. We present Python method names using the following style: pyramid.config.Configurator.add_view() We present Python class names, module names, attributes and global variables using the following style: pyramid.config.Configurator.registry References to glossary terms are presented using the following style: Pylons URLs are presented using the following style: Pylons References to sections and chapters are presented using the following style: Traversal Code and conguration le blocks are presented in the following style:
1 2
Example blocks representing UNIX shell commands are prexed with a $ character, e.g.:
$ ../bin/nosetests
Example blocks representing Windows cmd.exe commands are prexed with a drive letter and/or a directory name, e.g.:
c:\examples> ..\Scripts\nosetests
Sometimes, when its unknown which directory is current, Windows cmd.exe example block commands are prexed only with a > character, e.g.:
> ..\Scripts\nosetests
When a command that should be typed on one line is too long to t on a page, the backslash \ is used to indicate that the following printed line should actually be part of the command:
c:\bigfntut\tutorial> ..\Scripts\nosetests --cover-package=tutorial \ --cover-erase --with-coverage
A sidebar, which presents a concept tangentially related to content discussed on a page, is rendered like so:
vi
AUTHOR INTRODUCTION
Welcome to The Pyramid Web Application Framework. In this introduction, Ill describe the audience for this book, Ill describe the book content, Ill provide some context regarding the genesis of Pyramid, and Ill thank some important people. I hope you enjoy both this book and the software it documents. Ive had a blast writing both.
Audience
This book is aimed primarily at a reader that has the following attributes: At least a moderate amount of Python experience. A familiarity with web protocols such as HTTP and CGI. If you t into both of these categories, youre in the direct target audience for this book. But dont worry, even if you have no experience with Python or the web, both are easy to pick up on the y. Python is an excellent language in which to write applications; becoming productive in Python is almost mind-blowingly easy. If you already have experience in another language such as Java, Visual Basic, Perl, Ruby, or even C/C++, learning Python will be a snap; it should take you no longer than a couple of days to become modestly productive. If you dont have previous programming experience, it will be slightly harder, and it will take a little longer, but youd be hard-pressed to nd a better rst language. Web technology familiarity is assumed in various places within the book. For example, the book doesnt try to dene common web-related concepts like URL or query string. Likewise, the book describes various interactions in terms of the HTTP protocol, but it does not describe how the HTTP protocol works in detail. Like any good web framework, though, Pyramid shields you from needing to know most of the gory details of web protocols and low-level data structures. As a result, you can usually avoid becoming blocked while you read this book even if you dont yet deeply understand web technologies.
vii
Book Content
This book is divided into three major parts: Narrative Documentation This is documentation which describes Pyramid concepts in narrative form, written in a largely conversational tone. Each narrative documentation chapter describes an isolated Pyramid concept. You should be able to get useful information out of the narrative chapters if you read them out-of-order, or when you need only a reminder about a particular topic while youre developing an application. Tutorials Each tutorial builds a sample application or implements a set of concepts with a sample; it then describes the application or concepts in terms of the sample. You should read the tutorials if you want a guided tour of Pyramid. API Reference Comprehensive reference material for every public API exposed by Pyramid. The API documentation is organized alphabetically by module name.
package, and it emulates the normal Zope 2 publisher pretty well. But during its development, it became clear that Zope 2 had simply begun to exceed my tolerance for complexity, and I began to look around for simpler options. I considered using the Zope 3 application server machinery, but it turned out that it had become more indirect than the Zope 2 machinery it aimed to replace, which didnt fulll the goal of simplication. I also considered using Django and Pylons, but neither of those frameworks offer much along the axes of traversal, contextual declarative security, or application extensibility; these were features I had become accustomed to as a Zope developer. I decided that in the long term, creating a simpler framework that retained features I had become accustomed to when developing Zope applications was a more reasonable idea than continuing to use any Zope publisher or living with the limitations and unfamiliarities of a different framework. The result is what is now Pyramid.
Thanks
This book is dedicated to my grandmother, who gave me my rst typewriter (a Royal), and my mother, who bought me my rst computer (a VIC-20). Thanks to the following people for providing expertise, resources, and software. Without the help of these folks, neither this book nor the software which it details would exist: Paul Everitt, Tres Seaver, Andrew Sawyers, Malthe Borch, Carlos de la Guardia, Chris Rossi, Shane Hathaway, Daniel Holth, Wichert Akkerman, Georg Brandl, Blaise Laamme, Ben Bangert, Casey Duncan, Hugues Laamme, Mike Orr, John Shipman, Chris Beelby, Patricio Paez, Simon Oram, Nat Hardwick, Ian Bicking, Jim Fulton, Michael Merickel, Tom Moroz of the Open Society Institute, and Todd Koym of Environmental Health Sciences. Thanks to Guido van Rossum and Tim Peters for Python. Special thanks to Tricia for putting up with me.
ix
Part I
Narrative Documentation
CHAPTER
ONE
PYRAMID INTRODUCTION
Pyramid is a general, open source, Python web application development framework. Its primary goal is to make it easier for a Python developer to create web applications.
Frameworks vs. Libraries A framework differs from a library in one very important way: library code is always called by code that you write, while a framework always calls code that you write. Using a set of libraries to create an application is usually easier than using a framework initially, because you can choose to cede control to library code you have not authored very selectively. But when you use a framework, you are required to cede a greater portion of control to code you have not authored: code that resides in the framework itself. You neednt use a framework at all to create a web application using Python. A rich set of libraries already exists for the platform. In practice, however, using a framework to create an application is often more practical than rolling your own via a set of libraries if the framework provides a set of facilities that ts your application requirements.
Pyramid attempts to follow these design and engineering principles: Simplicity Pyramid takes a pay only for what you eat approach. You can get results even if you have only a partial understanding of Pyramid. It doesnt force you to use any particular technology to produce an application, and we try to keep the core set of concepts that you need to understand to a minimum. Minimalism Pyramid tries to solve only the fundamental problems of creating a web application: the mapping of URLs to code, templating, security and serving static assets. We consider these to be the core activities that are common to nearly all web applications. 3
1. PYRAMID INTRODUCTION Documentation Pyramids minimalism means that it is easier for us to maintain complete and up-to-date documentation. It is our goal that no aspect of Pyramid is undocumented. Speed Pyramid is designed to provide noticeably fast execution for common tasks such as templating and simple response generation. Although hardware is cheap, the limits of this approach become painfully evident when one nds him or herself responsible for managing a great many machines. Reliability Pyramid is developed conservatively and tested exhaustively. Where Pyramid source code is concerned, our motto is: If it aint tested, its broke. Openness As with Python, the Pyramid software is distributed under a permissive open source license.
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()
1. PYRAMID INTRODUCTION However, unlike some other systems, using decorators for Pyramid conguration does not make your application difcult to extend, test or reuse. The view_config decorator, for example, does not actually change the input or output of the function it decorates, so testing it is a WYSIWYG operation; you dont need to understand the framework to test your own code, you just behave as if the decorator is not there. You can also instruct Pyramid to ignore some decorators, or use completely imperative conguration instead of decorators to add views. Pyramid decorators are inert instead of eager: you detect and activate them with a scan. Example: Adding View Conguration Using the @view_cong Decorator.
1.1.7 Add-ons
Pyramid has an extensive set of add-ons held to the same quality standards as the Pyramid core itself. Addons are packages which provide functionality that the Pyramid core doesnt. Add-on packages already exist which let you easily send email, let you use the Jinja2 templating system, let you use XML-RPC or JSON-RPC, let you integrate with jQuery Mobile, etc. Examples: http://docs.pylonsproject.org/en/latest/docs/pyramid.html#pyramid-add-on-documentation
1. PYRAMID INTRODUCTION
1 2 3 4 5 6
from pyramid.response import Response from pyramid.view import view_config @view_config(route_name=aview) def aview(request): return Response(one)
from pyramid.response import Response from 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)
from pyramid.renderers import render_to_response def myview(request): return render_to_response(myapp:templates/mytemplate.pt, {a:1}, request=request)
When this view callable is called by Pyramid, the {a:1} dictionary will be rendered to a response on your behalf. The string passed as renderer= above is an asset specication. It is in the form packagename:directoryname/filename.ext. In this case, it refers to the mytemplate.pt le in the templates directory within the myapp Python package. Asset specications are omnipresent in Pyramid: see Asset specications for more information. Example: Renderers. 9
1. PYRAMID INTRODUCTION
Pyramid will add appropriate Cache-Control and Expires headers to responses generated when this view is invoked. See the add_view() methods http_cache documentation for more information.
10
1.1.15 Sessions
Pyramid has built-in HTTP sessioning. This allows you to associate data with otherwise anonymous users between requests. Lots of systems do this. But Pyramid also allows you to plug in your own sessioning system by creating some code that adheres to a documented interface. Currently there is a binding package for the third-party Beaker sessioning system that does exactly this. But if you have a specialized need (perhaps you want to store your session data in MongoDB), you can. You can even switch between implementations without changing your application code. Example: Sessions.
1.1.16 Speed
The Pyramid core is, as far as we can tell, at least marginally faster than any other existing Python web framework. It has been engineered from the ground up for speed. It only does as much work as absolutely necessary when you ask it to get a job done. Extraneous function calls and suboptimal algorithms in its core codepaths are avoided. It is feasible to get, for example, between 3500 and 4000 requests per second from a simple Pyramid view on commodity dual-core laptop hardware and an appropriate WSGI server (mod_wsgi or gunicorn). In any case, performance statistics are largely useless without requirements and goals, but if you need speed, Pyramid will almost certainly never be your applications bottleneck; at least no more than Python will be a bottleneck. Example: http://blog.curiasolutions.com/the-great-web-framework-shootout/
11
1. PYRAMID INTRODUCTION
1.1.18 No singletons
Pyramid is written in such a way that it requires your application to have exactly zero singleton data structures. Or, put another way, Pyramid doesnt require you to construct any mutable globals. Or put even a different way, an import of a Pyramid application neednt have any import-time side effects. This is esoteric-sounding, but if youve ever tried to cope with parameterizing a Django settings.py le for multiple installations of the same application, or if youve ever needed to monkey-patch some framework xture so that it behaves properly for your use case, or if youve ever wanted to deploy your system using an asynchronous server, youll end up appreciating this feature. It just wont be a problem. You can even run multiple copies of a similar but not identically congured Pyramid application within the same Python process. This is good for shared hosting environments, where RAM is at a premium.
1.1. WHAT MAKES PYRAMID UNIQUE Also, Pyramids transaction management system 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. Example: SQLAlchemy + URL Dispatch Wiki Tutorial (note the lack of commit statements anywhere in application code).
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)
See also Including Conguration from External Sources and Rules for Building An Extensible Application 13
1. PYRAMID INTRODUCTION
1.1.24 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. Each of those resources can have one or more views associated with it. If your data isnt naturally treelike (or youre unwilling to create a treelike representation of your data), you arent going to nd traversal very useful. However, traversal is absolutely fantastic for sites that need to be arbitrarily extensible: its a lot easier to add a node to a tree than it is to shoehorn a route into an ordered list of other routes, or to create another entire instance of an application to service a department and glue code to allow disparate apps to share data. Its a great t for sites that naturally lend themselves to changing departmental hierarchies, such as content management systems and document management systems. Traversal also lends itself well to systems that require very granular security (Bob can edit this document as opposed to Bob can edit documents). Examples: hello_traversal_chapter and Much Ado About Traversal.
1.1.25 Tweens
Pyramid has a sort of internal WSGI-middleware-ish pipeline that can be hooked by arbitrary add-ons named tweens. The debug toolbar is a tween, and the pyramid_tm transaction manager is also. Tweens are more useful than WSGI middleware in some circumstances because they run in the context of Pyramid itself, meaning you have access to templates and other renderers, a real request object, and other niceties. Example: Registering Tweens.
14
To this:
1 2 3 4
The former is prettier, right? Out of the box, if you dene the former view callable (the one that simply returns a string) in Pyramid, when it is executed, Pyramid will raise an exception. This is because explicit is better than implicit, in most cases, and by default, Pyramid wants you to return a Response object from a view callable. This is because theres usually a heck of a lot more to a response object than just its body. But if youre the kind of person who values such aesthetics, we have an easy way to allow for this sort of thing:
1 2 3 4 5 6 7 8 9 10 11
from pyramid.config import Configurator from 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)
Do that once in your Pyramid application at startup. Now you can return strings from any of your view callables, e.g.:
15
1. PYRAMID INTRODUCTION
1 2 3 4 5
def helloview(request): return "Hello world!" def goodbyeview(request): return "Goodbye world!"
Oh noes! What if you want to indicate a custom content type? And a custom status code? No fear:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
from pyramid.config import Configurator def tuple_response_adapter(val): status_int, content_type, body = val response = Response(body) response.content_type = content_type response.status_int = status_int return response def string_response_adapter(body): response = Response(body) response.content_type = text/html response.status_int = 200 return response if __name__ == __main__: config = Configurator() config.add_response_adapter(string_response_adapter, basestring) config.add_response_adapter(tuple_response_adapter, tuple)
def aview(request): return "Hello world!" def anotherview(request): return (403, text/plain, "Forbidden")
Pyramid defaults to explicit behavior, because its the most generally useful, but provides hooks that allow you to adapt the framework to localized aesthetic desires. See also Changing How Pyramid Treats View Responses.
16
def aview(request): response = request.response response.body = Hello world! response.content_type = text/plain return response
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)
Pretty tedious right? You can add a directive to the Pyramid congurator to automate some of the tedium away:
1 2 3 4 5
from pyramid.config import Configurator def add_protected_xhr_views(config, module): module = config.maybe_dotted(module) for method in (GET, POST, HEAD):
17
1. PYRAMID INTRODUCTION
6 7 8 9 10 11 12
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)
Once thats done, you can call the directive youve just added as a method of the Congurator object:
1 2
Your previously repetitive conguration lines have now morphed into one line. You can share your conguration code with others this way too by packaging it up and calling add_directive() from within a function called when another user uses the include() method against your code. See also Adding Methods to the Congurator via add_directive.
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]))
1.1.31 Testing
Every release of Pyramid has 100% statement coverage via unit and integration tests, as measured by the coverage tool available on PyPI. It also has greater than 95% decision/condition coverage as measured by the instrumental tool available on PyPI. It is automatically tested by the Jenkins tool on Python 2.6, Python 2.7, Python 3.2 and PyPy after each commit to its GitHub repository. Ofcial Pyramid addons are held to a similar testing standard. We still nd bugs in Pyramid and its ofcial add-ons, but weve noticed we nd a lot more of them while working on other projects that dont have a good testing regime. Example: http://jenkins.pylonsproject.org/
1.1.32 Support
Its our goal that no Pyramid question go unanswered. Whether you ask a question on IRC, on the Pylonsdiscuss maillist, or on StackOverow, youre likely to get a reasonably prompt response. We dont tolerate support trolls or other people who seem to get their rocks off by berating fellow users in our various ofcal support channels. We try to keep it well-lit and new-user-friendly. Example: Visit irc://freenode.net#pyramid (the #pyramid channel on irc.freenode.net in an IRC client) or the pylons-discuss maillist at http://groups.google.com/group/pylons-discuss/ .
1.1.33 Documentation
Its a constant struggle, but we try to maintain a balance between completeness and new-user-friendliness in the ofcial narrative Pyramid documentation (concrete suggestions for improvement are always appreciated, by the way). We also maintain a cookbook of recipes, which are usually demonstrations of common integration scenarios, too specic to add to the ofcial narrative docs. In any case, the Pyramid documentation is comprehensive. Example: The rest of this documentation http://docs.pylonsproject.org/projects/pyramid_cookbook/dev/ . 19 and the cookbook at
1. PYRAMID INTRODUCTION
You Say Pyramid is MVC, But Wheres The Controller? The Pyramid authors believe that the MVC pattern just doesnt really t the web very well. In a Pyramid application, there is a resource tree, which represents the site structure, and views, which tend to present the data stored in the resource tree and a user-dened domain model. However, no facility provided by the framework actually necessarily maps to the concept of a controller or model. So if you had to give it some acronym, I guess youd say Pyramid is actually an RV framework rather than an MVC framework. MVC, however, is close enough as a general classication moniker for purposes of comparison with other web frameworks.
21
1. PYRAMID INTRODUCTION
22
CHAPTER
TWO
INSTALLING PYRAMID
2.1 Before You Install
You will need Python version 2.6 or better to run Pyramid.
Python Versions As of this writing, Pyramid has been tested under Python 2.6.8, Python 2.7.3, Python 3.2.3, and Python 3.3b1. Pyramid does not run under any version of Python before 2.6.
Pyramid is known to run on all popular UNIX-like systems such as Linux, MacOS X, and FreeBSD as well as on Windows platforms. It is also known to run on PyPy (1.9+). Pyramid installation does not require the compilation of any C code, so you need only a Python interpreter that meets the requirements mentioned.
23
2. INSTALLING PYRAMID Package Manager Method You can use your systems package manager to install Python. Every systems package manager is slightly different, but the avor of them is usually the same. For example, on an Ubuntu Linux system, to use the system package manager to install a Python 2.7 interpreter, use the following command:
$ sudo apt-get install python2.7-dev
This command will install both the Python interpreter and its development header les. Note that the headers are required by some (optional) C extensions in software depended upon by Pyramid, not by Pyramid itself. Once these steps are performed, the Python interpreter will usually be invokable via python2.7 from a shell prompt.
Source Compile Method Its useful to use a Python interpreter that isnt the system Python interpreter to develop your software. The authors of Pyramid tend not to use the system Python for development purposes; always a selfcompiled one. Compiling Python is usually easy, and often the system Python is compiled with options that arent optimal for web development. To compile software on your UNIX system, typically you need development tools. Often these can be installed via the package manager. For example, this works to do so on an Ubuntu Linux system:
$ sudo apt-get install build-essential
On Mac OS X, installing XCode has much the same effect. Once youve got development tools installed on your system, you can install a Python 2.7 interpreter from source, on the same system, using the following commands:
[chrism@vitaminf ~]$ cd ~ [chrism@vitaminf ~]$ mkdir tmp [chrism@vitaminf ~]$ mkdir opt [chrism@vitaminf ~]$ cd tmp [chrism@vitaminf tmp]$ wget \ http://www.python.org/ftp/python/2.7.3/Python-2.7.3.tgz
24
[chrism@vitaminf tmp]$ tar xvzf Python-2.7.3.tgz [chrism@vitaminf tmp]$ cd Python-2.7.3 [chrism@vitaminf Python-2.7.3]$ ./configure \ --prefix=$HOME/opt/Python-2.7.3 [chrism@vitaminf Python-2.7.3]$ make; make install
Once these steps are performed, the Python interpreter $HOME/opt/Python-2.7.3/bin/python from a shell prompt.
will
be
invokable
via
After you install Python on Windows, you may need to add the C:\Python27 directory to your environments Path in order to make it possible to invoke Python from a command prompt by typing python. To do so, right click My Computer, select Properties > Advanced Tab > Environment Variables and add that directory to the end of the Path environment variable.
25
2. INSTALLING PYRAMID
[chrism@thinko docs]$ python2.7 Python 2.7.3 (default, Aug 1 2012, 05:14:39) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import setuptools >>>
Heres the output you can expect if setuptools or distribute is not already installed:
[chrism@thinko docs]$ python2.7 Python 2.7.3 (default, Aug 1 2012, 05:14:39) [GCC 4.6.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> import setuptools Traceback (most recent call last): File "<stdin>", line 1, in <module> ImportError: No module named setuptools >>>
If import setuptools raises an ImportError as it does above, you will need to install setuptools or distribute manually. Note that above were using a Python 2.7-series interpreter on Mac OS X; your output may differ if youre using a later Python version or a different platform. If you are using a system Python (one installed by your OS distributor or a 3rd-party packager such as Fink or MacPorts), you can usually install the setuptools or distribute package by using your systems package manager. If you cannot do this, or if youre using a self-installed version of Python, you will need to install setuptools or distribute by hand. Installing setuptools or distribute by hand is always a reasonable thing to do, even if your package manager already has a pre-chewed version of setuptools for installation. If youre using Python 2, youll want to install setuptools. If youre using Python 3, youll want to install distribute. Below we tell you how to do both.
26
2.2. INSTALLING PYRAMID ON A UNIX SYSTEM Once this command is invoked, setuptools should be installed on your system. If the command fails due to permission errors, you may need to be the administrative user on your system to successfully invoke the script. To remediate this, you may need to do:
$ sudo python ez_setup.py
Once this command is invoked, distribute should be installed on your system. If the command fails due to permission errors, you may need to be the administrative user on your system to successfully invoke the script. To remediate this, you may need to do:
$ sudo python3 distribute_setup.py
$ easy_install virtualenv
This command should succeed, and tell you that the virtualenv package is now installed. If it fails due to permission errors, you may need to install it as your systems administrative user. For example: 27
2. INSTALLING PYRAMID
Using --no-site-packages when generating your virtualenv is very important. This ag provides the necessary isolation for running the set of packages required by Pyramid. If you do not specify --no-site-packages, its possible that Pyramid will not install properly into the virtualenv, or, even if it does, may not run properly, depending on the packages youve already got installed into your Pythons main site-packages dir.
do not use sudo to run the virtualenv script. Its perfectly acceptable (and desirable) to create a virtualenv as a normal user. You should perform any following commands that mention a bin directory from within the env virtualenv dir.
The easy_install command will take longer than the previous ones to complete, as it downloads and installs a number of dependencies. 28
7. (Optional) Consider using Scripts\activate.bat to make your shell environment wired to use the virtualenv. 8. Use easy_install to get Pyramid and its direct dependencies installed:
c:\env> Scripts\easy_install pyramid
29
2. INSTALLING PYRAMID
7. (Optional) Consider using Scripts\activate.bat to make your shell environment wired to use the virtualenv. 8. Use easy_install to get Pyramid and its direct dependencies installed:
c:\env> Scripts\easy_install pyramid
CHAPTER
THREE
APPLICATION CONFIGURATION
Most people already understand conguration as settings that inuence the operation of an application. For instance, its easy to think of the values in a .ini le parsed at application startup time as conguration. However, if youre reasonably open-minded, its easy to think of code as conguration too. Since Pyramid, like most other web application platforms, is a framework, it calls into code that you write (as opposed to a library, which is code that exists purely for you to call). The act of plugging application code that youve written into Pyramid is also referred to within this documentation as conguration; you are conguring Pyramid to call the code that makes up your application. There are two ways to congure a Pyramid application: imperative conguration and declarative conguration. Both are described below.
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response(Hello world!) if __name__ == __main__: config = Configurator() config.add_view(hello_world) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
31
3. APPLICATION CONFIGURATION We wont talk much about what this application does yet. Just note that the conguration statements take place underneath the if __name__ == __main__: stanza in the form of method calls on a Congurator object (e.g. config.add_view(...)). These statements take place one after the other, and are executed in order, so the full power of Python, including conditionals, can be employed in this mode of conguration.
from pyramid.response import Response from pyramid.view import view_config @view_config(name=hello, request_method=GET) def hello(request): return Response(Hello)
The mere existence of conguration decoration doesnt cause any conguration registration to be performed. Before it has any effect on the conguration of a Pyramid application, a conguration decoration within application code must be found through a process known as a scan. For example, the pyramid.view.view_config decorator in the code example above adds an attribute to the hello function, making it available for a scan to nd it later. A scan of a module or a package and its subpackages for decorations happens when the pyramid.config.Configurator.scan() method is invoked: scanning implies searching for conguration declarations in a package and its subpackages. For example:
32
3.3. SUMMARY
Starting A Scan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
wsgiref.simple_server import make_server pyramid.config import Configurator pyramid.response import Response pyramid.view import view_config
@view_config() def hello(request): return Response(Hello) if __name__ == __main__: from pyramid.config import Configurator config = Configurator() config.scan() app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
The scanning machinery imports each module and subpackage in a package or module recursively, looking for special attributes attached to objects dened within a module. These special attributes are typically attached to code via the use of a decorator. For example, the view_config decorator can be attached to a function or instance method. Once scanning is invoked, and conguration decoration is found by the scanner, a set of calls are made to a Congurator on your behalf: these calls replace the need to add imperative conguration statements that dont live near the code being congured. The combination of conguration decoration and the invocation of a scan is collectively known as declarative conguration. In the example above, the scanner translates the arguments to view_config into a call to the pyramid.config.Configurator.add_view() method, effectively:
1
config.add_view(hello)
3.3 Summary
There are two ways to congure a Pyramid application: declaratively and imperatively. You can choose the mode youre most comfortable with; both are completely equivalent. Examples in this documentation will use both modes interchangeably. 33
3. APPLICATION CONFIGURATION
34
CHAPTER
FOUR
from wsgiref.simple_server import make_server from pyramid.config import Configurator from 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()
35
4. CREATING YOUR FIRST PYRAMID APPLICATION When this code is inserted into a Python script named helloworld.py and executed by a Python interpreter which has the Pyramid software installed, an HTTP server is started on TCP port 8080. On UNIX:
$ /path/to/your/virtualenv/bin/python helloworld.py
On Windows:
C:\> \path\to\your\virtualenv\Scripts\python.exe helloworld.py
This command will not return and nothing will be printed to the console. When port 8080 is visited by a browser on the URL /hello/world, the server will simply serve up the text Hello world!. If your application is running on your local system, using http://localhost:8080/hello/world in a browser will show this result. Each time you visit a URL served by the application in a browser, a logging line will be emitted to the console displaying the hostname, the date, the request method and path, and some additional information. This output is done by the wsgiref server weve used to serve this application. It logs an access log in Apache combined logging format to the console. Press Ctrl-C (or Ctrl-Break on Windows) to stop the application. Now that we have a rudimentary understanding of what the application does, lets examine it piece-bypiece.
4.1.1 Imports
The above helloworld.py script uses the following set of import statements:
1 2 3
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response
The script imports the Configurator class from the pyramid.config module. An instance of the Configurator class is later used to congure your Pyramid application. Like many other Python web frameworks, Pyramid uses the WSGI protocol to connect an application and a web server together. The wsgiref server is used in this example as a WSGI server for convenience, as it is shipped within the Python standard library. The script also imports the pyramid.response.Response class for later use. An instance of this class will be used to create a web response. 36
The function accepts a single argument (request) and it returns an instance of the pyramid.response.Response class. The single argument to the class constructor is a string computed from parameters matched from the URL. This value becomes the body of the response. This function is known as a view callable. A view callable accepts a single argument, request. It is expected to return a response object. A view callable doesnt need to be a function; it can be represented via another type of object, like a class or an instance, but for our purposes here, a function serves us well. A view callable is always called with a request object. A request object is a representation of an HTTP request sent to Pyramid via the active WSGI server. A view callable is required to return a response object because a response object has all the information necessary to formulate an actual HTTP response; this object is then converted to text by the WSGI server which called Pyramid and it is sent back to the requesting browser. To return a response, each view callable creates an instance of the Response class. In the hello_world function, a string is passed as the body to the response.
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()
37
1 2
The if __name__ == __main__: line in the code sample above represents a Python idiom: the code inside this if clause is not invoked unless the script containing this code is run directly from the operating system command line. For example, if the le named helloworld.py contains the entire script body, the code within the if statement will only be invoked when python helloworld.py is executed from the command line. Using the if clause is necessary or at least best practice because code in a Python .py le may be eventually imported via the Python import statement by another .py le. .py les that are imported by other .py les are referred to as modules. By using the if __name__ == __main__: idiom, the script above is indicating that it does not want the code within the if statement to execute if this module is imported from another; the code within the if block should only be run during a direct script execution. The config = Configurator() line above creates an instance of the Configurator class. The resulting config object represents an API which the script uses to congure this particular Pyramid application. Methods called on the Congurator will cause registrations to be made in an application registry associated with the application.
First line above calls the pyramid.config.Configurator.add_route() method, which registers a route to match any URL path that begins with /hello/ followed by a string. The second line, config.add_view(hello_world, route_name=hello), registers the hello_world function as a view callable and makes sure that it will be called when the hello route is matched.
app = config.make_wsgi_app()
38
4.1. HELLO WORLD After conguring views and ending conguration, the script creates a WSGI application via the pyramid.config.Configurator.make_wsgi_app() method. A call to make_wsgi_app implies that all conguration is nished (meaning all method calls to the congurator which set up views, and various other conguration settings have been performed). The make_wsgi_app method returns a WSGI application object that can be used by any WSGI server to present an application to a requestor. WSGI is a protocol that allows servers to talk to Python applications. We dont discuss WSGI in any depth within this book, however, you can learn more about it by visiting wsgi.org. The Pyramid application object, in particular, is an instance of a class representing a Pyramid router. It has a reference to the application registry which resulted from method calls to the congurator used to congure it. The router consults the registry to obey the policy choices made by a single application. These policy choices were informed by method calls to the Congurator made earlier; in our case, the only policy choices made were implied by calls to its add_view and add_route methods.
Finally, we actually serve the application to requestors by starting up a WSGI server. We happen to use the wsgiref make_server server maker for this purpose. We pass in as the rst argument 0.0.0.0, which means listen on all TCP interfaces. By default, the HTTP server listens only on the 127.0.0.1 interface, which is problematic if youre running the server on a remote system and you wish to access it with a web browser from a local system. We also specify a TCP port number to listen on, which is 8080, passing it as the second argument. The nal argument is the app object (a router), which is the application we wish to serve. Finally, we call the servers serve_forever method, which starts the main loop in which it will wait for requests from the outside world. When this line is invoked, it causes the server to start listening on TCP port 8080. The server will serve requests forever, or at least until we stop it by killing the process which runs it (usually by pressing Ctrl-C or Ctrl-Break in the terminal we used to start it).
4.1.8 Conclusion
Our hello world application is one of the simplest possible Pyramid applications, congured imperatively. We can see that its congured imperatively because the full power of Python is available to us as we perform conguration tasks.
39
4.2 References
For more information about the API of a Congurator object, see Configurator . For more information about view conguration, see View Conguration.
40
CHAPTER
FIVE
Or on Windows:
> Scripts\pcreate -s starter MyProject
The above command uses the pcreate command to create a project with the starter scaffold. To use a different scaffold, such as alchemy, youd just change the -s argument value. For example, on UNIX:
$ bin/pcreate -s alchemy MyProject
Or on Windows:
> Scripts\pcreate -s alchemy MyProject
Heres sample output from a run of pcreate on UNIX for a project we name MyProject:
$ bin/pcreate -s starter MyProject Creating template pyramid Creating directory ./MyProject # ... more output ... Running /Users/chrism/projects/pyramid/bin/python setup.py egg_info
42
5.3. INSTALLING YOUR NEWLY CREATED PROJECT FOR DEVELOPMENT As a result of invoking the pcreate command, a directory named MyProject is created. That directory is a project directory. The setup.py le in that directory can be used to distribute your application, or install your application for deployment or development. A .ini le named development.ini will be created in the project directory. You will use this .ini le to congure a server, to run your application, and to debug your application. It contains conguration that enables an interactive debugger and settings optimized for development. Another .ini le named production.ini will also be created in the project directory. It contains conguration that disables any interactive debugger (to prevent inappropriate access and disclosure), and turns off a number of debugging settings. You can use this le to put your application into production. The MyProject project directory contains an additional subdirectory named myproject (note the case difference) representing a Python package which holds very simple Pyramid sample code. This is where youll edit your applications Python code and templates. We created this project within an env virtualenv directory. However, note that this is not mandatory. The project directory can go more or less anywhere on your lesystem. You dont need to put it in a special web server directory, and you dont need to put it within a virtualenv directory. The author uses Linux mainly, and tends to put project directories which he creates within his ~/projects directory. On Windows, its a good idea to put project directories within a directory that contains no space characters, so its wise to avoid a path that contains i.e. My Documents. As a result, the author, when he uses Windows, just puts his projects in C:\projects. Youll need to avoid using pcreate to create a project with the same name as a Python standard library component. In particular, this means you should avoid using the names site or test, both of which conict with Python standard library packages. You should also avoid using the name pyramid, which will conict with Pyramid itself.
Or on Windows:
> cd MyProject > ..\Scripts\python.exe setup.py develop
This will install a distribution representing your project into the interpreters library set so it can be found by import statements and by other console scripts such as pserve, pshell, proutes and pviews.
Or on Windows:
> ..\Scripts\python.exe setup.py test -q
44
$ ../bin/python setup.py test -q running test running egg_info writing requirements to MyProject.egg-info/requires.txt writing MyProject.egg-info/PKG-INFO writing top-level names to MyProject.egg-info/top_level.txt writing dependency_links to MyProject.egg-info/dependency_links.txt writing entry points to MyProject.egg-info/entry_points.txt reading manifest file MyProject.egg-info/SOURCES.txt writing manifest file MyProject.egg-info/SOURCES.txt running build_ext .. ---------------------------------------------------------------------Ran 1 test in 0.108s OK
The -q option is passed to the setup.py test command to limit the output to a stream of dots. If you dont pass -q, youll see more verbose test result output (which normally isnt very useful). The tests themselves are found in the tests.py module in your pcreate generated project. Within a project generated by the starter scaffold, a single sample test exists.
On Windows:
> ..\Scripts\pserve development.ini
45
When you use pserve to start the application implied by the default rendering of a scaffold, it will respond to requests on all IP addresses possessed by your system, not just requests to localhost. This is what the 0.0.0.0 in serving on http://0.0.0.0:6543 means. The server will respond to requests made to 127.0.0.1 and on any external IP address. For example, your system might be congured to have an external IP address 192.168.1.50. If thats the case, if you use a browser running on the same system as Pyramid, it will be able to access the application via http://127.0.0.1:6543/ as well as via http://129.168.1.50:6543/. However, other people on other computers on the same network will also be able to visit your Pyramid application in their browser by visiting http://192.168.1.50:6543/. If you want to restrict access such that only a browser running on the same machine as Pyramid will be able to access your Pyramid application, edit the development.ini le, and replace the host value in the [server:main] section. Change it from 0.0.0.0 to 127.0.0.1. For example:
[server:main] use = egg:waitress#main host = 127.0.0.1 port = 6543
You can change the port on which the server runs on by changing the same portion of the development.ini le. For example, you can change the port = 6543 line in the development.ini les [server:main] section to port = 8080 to run the server on port 8080 instead of port 6543. You can shut down a server started this way by pressing Ctrl-C. The default server used to run your Pyramid application when a project is created from a scaffold is named Waitress. This server is what prints the serving on... line when you run pserve. Its a good idea to use this server during development, because its very simple. It can also be used for light production. Setting your application up under a different server is not advised until youve done some development work under the default server, particularly if youre not yet experienced with Python web development. Python web server setup can be complex, and you should get some condence that your application works in a default environment before trying to optimize it or make it more like production. Its awfully easy to get sidetracked trying to set up a nondefault server for hours without actually starting to do any development. One of the nice things about Python web servers is that theyre largely interchangeable, so if your application works under the default server, it will almost certainly work under any other server in production if you eventually choose to use a different one. Dont worry about it right now. For more detailed information about the startup process, see Startup. For more information about environment variables and conguration le settings that inuence startup and runtime behavior, see Environment Variables and .ini File Settings. 46
Now if you make a change to any of your projects .py les or .ini les, youll see the server restart automatically:
development.ini changed; reloading... -------------------- Restarting -------------------Starting server in PID 16602. serving on http://0.0.0.0:6543
Changes to template les (such as .pt or .mak les) wont cause the server to restart. Changes to template les dont require a server restart as long as the pyramid.reload_templates setting in the development.ini le is true. Changes made to template les when this setting is true will take effect immediately without a server restart.
47
This is the page shown by default when you visit an unmodied pcreate generated starter application in a browser.
48
If you dont see the debug toolbar image on the right hand top of the page, it means youre browsing from a system that does not have debugging access. By default, for security reasons, only a browser originating from localhost (127.0.0.1) can see the debug toolbar. To allow your browser on a remote system to access the server, add the a line within the [app:main] section of the development.ini le in the form debugtoolbar.hosts = X.X.X.X. For example, if your Pyramid application is running on a remote system, and youre browsing from a host with the IP address 192.168.1.1, youd add something like this to enable the toolbar when your system contacts Pyramid:
[app:main] # .. other settings ... debugtoolbar.hosts = 192.168.1.1
For more information about what the debug toolbar allows you to do, see the documentation for pyramid_debugtoolbar. 49
5. CREATING A PYRAMID PROJECT The debug toolbar will not be shown (and all debugging will be turned off) when you use the production.ini le instead of the development.ini ini le to run the application. You can also turn the debug toolbar off by editing development.ini and commenting out a line. For example, instead of:
[app:main] ... pyramid.includes = pyramid_debugtoolbar
Then restart the application to see that the toolbar has been turned off. Note that if you comment out the pyramid_debugtoolbar line, the # must be in the rst column. If you put the hash mark anywhere except the rst column instead, for example like this:
[app:main] ... pyramid.includes = #pyramid_debugtoolbar
When you attempt to restart the application with a section like the above youll receive an error that ends something like this, and the application will not start:
ImportError: No module named #pyramid_debugtoolbar
MyProject/ |-- CHANGES.txt |-- development.ini |-- MANIFEST.in |-- myproject | |-- __init__.py | |-- static | | |-- favicon.ico | | |-- logo.png | | -- pylons.css | |-- templates | | -- mytemplate.pt | |-- tests.py | -- views.py |-- production.ini |-- README.txt |-- setup.cfg -- setup.py
5.8.1 development.ini
The development.ini le is a PasteDeploy conguration le. Its purpose is to specify an application to run when you invoke pserve, as well as the deployment settings provided to that application. The generated development.ini le looks like so:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
[app:main] use = egg:MyProject 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 [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 # Begin logging configuration [loggers] keys = root, myproject [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_myproject] level = DEBUG handlers = qualname = myproject [handler_console] class = StreamHandler args = (sys.stderr,)
52
40 41 42 43 44 45 46
level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s # End logging configuration
This le contains several sections including [app:main], [server:main] and several other sections related to logging conguration. The [app:main] section represents conguration for your Pyramid application. The use setting is the only setting required to be present in the [app:main] section. Its default value, egg:MyProject, indicates that our MyProject project contains the application that should be served. Other settings added to this section are passed as keyword arguments to the function named main in our packages __init__.py module. You can provide startup-time conguration parameters to your application by adding more settings to this section.
See Entry Points and PasteDeploy .ini Files for more information about the meaning of the use = egg:MyProject value in this section.
The pyramid.reload_templates setting in the [app:main] section is a Pyramid -specic setting which is passed into the framework. If it exists, and its value is true, Chameleon and Mako template changes will not require an application restart to be detected. See Automatically Reloading Templates for more information.
The pyramid.reload_templates option should be turned off for production applications, as template rendering is slowed when it is turned on.
The pyramid.includes setting in the [app:main] section tells Pyramid to include conguration from another package. In this case, the line pyramid.includes = pyramid_debugtoolbar tells Pyramid to include conguration from the pyramid_debugtoolbar package. This turns on a debugging panel in development mode which will be shown on the right hand side of the screen. Including the debug toolbar will also make it possible to interactively debug exceptions when an error occurs. Various other settings may exist in this section having to do with debugging or inuencing runtime behavior of a Pyramid application. See Environment Variables and .ini File Settings for more information about these settings.
53
5. CREATING A PYRAMID PROJECT The name main in [app:main] signies that this is the default application run by pserve when it is invoked against this conguration le. The name main is a convention used by PasteDeploy signifying that it is the default application. The [server:main] section of the conguration le congures a WSGI server which listens on TCP port 6543. It is congured to listen on all interfaces (0.0.0.0). This means that any remote system which has TCP access to your system can see your Pyramid application. The sections that live between the markers # Begin logging configuration and # End logging configuration represent Pythons standard library logging module conguration for your application. The sections between these two markers are passed to the logging modules cong le conguration engine when the pserve or pshell commands are executed. The default conguration sends application logging output to the standard error output of your terminal. For more information about logging conguration, see Logging. See the PasteDeploy documentation for more information about other types of things you can put into this .ini le, such as other applications, middleware and alternate WSGI server implementations.
5.8.2 production.ini
The production.ini le is a PasteDeploy conguration le with a purpose much like that of development.ini. However, it disables the debug toolbar, and lters all log messages except those above the WARN level. It also turns off template development options such that templates are not automatically reloaded when changed, and turns off all debugging options. This le is appropriate to use instead of development.ini when you put your application into production. Its important to use production.ini (and not development.ini) to benchmark your application and put it into production. development.ini congures your system with a debug toolbar that helps development, but the inclusion of this toolbar slows down page rendering times by over an order of magnitude. The debug toolbar is also a potential security risk if you have it congured incorrectly.
5.8.3 MANIFEST.in
The MANIFEST.in le is a distutils conguration le which species the non-Python les that should be included when a distribution of your Pyramid project is created when you run python setup.py sdist. Due to the information contained in the default MANIFEST.in, an sdist of your Pyramid project will include .txt les, .ini les, .rst les, graphics les, and template les, as well as .py les. See http://docs.python.org/distutils/sourcedist.html#the-manifest-in-template for more information about the syntax and usage of MANIFEST.in. 54
5.8. THE MYPROJECT PROJECT Without the presence of a MANIFEST.in le or without checking your source code into a version control repository, setup.py sdist places only Python source les (les ending with a .py extension) into tarballs generated by python setup.py sdist. This means, for example, if your project was not checked into a setuptools-compatible source control system, and your project directory didnt contain a MANIFEST.in le that told the sdist machinery to include *.pt les, the myproject/templates/mytemplate.pt le would not be included in the generated tarball. Projects generated by Pyramid scaffolds include a default MANIFEST.in le. The MANIFEST.in le contains declarations which tell it to include les like *.pt, *.css and *.js in the generated tarball. If you include les with extensions other than the les named in the projects MANIFEST.in and you dont make use of a setuptools-compatible version control system, youll need to edit the MANIFEST.in le and include the statements necessary to include your new les. See http://docs.python.org/distutils/sourcedist.html#principle for more information about how to do this. You can also delete MANIFEST.in from your project and rely on a setuptools feature which simply causes all les checked into a version control system to be put into the generated tarball. To allow this to happen, check all the les that youd like to be distributed along with your applications Python les into Subversion. After you do this, when you rerun setup.py sdist, all les checked into the version control system will be included in the tarball. If you dont use Subversion, and instead use a different version control system, you may need to install a setuptools add-on such as setuptools-git or setuptools-hg for this behavior to work properly.
5.8.4 setup.py
The setup.py le is a setuptools setup le. It is meant to be run directly from the command line to perform a variety of functions, such as testing your application, packaging, and distributing your application.
setup.py is the de facto standard which Python developers use to distribute their reusable code. You can read more about setup.py les and their usage in the Setuptools documentation and The Hitchhikers Guide to Packaging.
import os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, README.txt)).read() CHANGES = open(os.path.join(here, CHANGES.txt)).read()
55
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
requires = [ pyramid, pyramid_debugtoolbar, waitress, ] setup(name=MyProject, version=0.0, description=MyProject, long_description=README + \n\n + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author=, author_email=, url=, keywords=web pyramid pylons, packages=find_packages(), include_package_data=True, zip_safe=False, install_requires=requires, tests_require=requires, test_suite="myproject", entry_points="""\ [paste.app_factory] main = myproject:main """, )
The setup.py le calls the setuptools setup function, which does various things depending on the arguments passed to setup.py on the command line. Within the arguments to this function call, information about your application is kept. While its beyond the scope of this documentation to explain everything about setuptools setup les, well provide a whirlwind tour of what exists in this le in this section. Your applications name can be any string; it is specied in the name eld. The version number is specied in the version value. A short description is provided in the description eld. The long_description is conventionally the content of the README and CHANGES le appended together. The classifiers eld is a list of Trove classiers describing your application. author and author_email are text elds which probably dont need any description. url is a eld that should 56
5.8. THE MYPROJECT PROJECT point at your application projects URL (if any). packages=find_packages() causes all packages within the project to be found when packaging the application. include_package_data will include non-Python les when the application is packaged if those les are checked into version control. zip_safe indicates that this package is not safe to use as a zipped egg; instead it will always unpack as a directory, which is more convenient. install_requires and tests_require indicate that this package depends on the pyramid package. test_suite points at the package for our application, which means all tests found in the package will be run when setup.py test is invoked. We examined entry_points in our discussion of the development.ini le; this le denes the main entry point that represents our projects application. Usually you only need to think about the contents of the setup.py le when distributing your application to other people, when adding Python package dependencies, or when versioning your application for your own use. For fun, you can try this command now:
$ python setup.py sdist
This will create a tarball of your application in a dist subdirectory named MyProject-0.1.tar.gz. You can send this tarball to other people who want to install and use your application.
5.8.5 setup.cfg
The setup.cfg le is a setuptools conguration le. It contains various settings related to testing and internationalization: Our generated setup.cfg looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
[nosetests] match = ^test nocapture = 1 cover-package = myproject with-coverage = 1 cover-erase = 1 [compile_catalog] directory = myproject/locale domain = MyProject statistics = true [extract_messages] add_comments = TRANSLATORS: output_file = myproject/locale/MyProject.pot width = 80
57
17 18 19 20 21 22 23 24 25 26 27
[init_catalog] domain = MyProject input_file = myproject/locale/MyProject.pot output_dir = myproject/locale [update_catalog] domain = MyProject input_file = myproject/locale/MyProject.pot output_dir = myproject/locale previous = true
The values in the default setup le allow various commonly-used internationalization commands and testing commands to work more smoothly.
5.9.1 __init__.py
We need a small Python module that congures our application and which advertises an entry point for use by our PasteDeploy .ini le. This is the le named __init__.py. The presence of an __init__.py also informs Python that the directory which contains it is a package.
58
1 2 3 4 5 6 7 8 9 10 11
def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ 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()
1. Line 1 imports the Congurator class from pyramid.config that we use later. 2. Lines 4-11 dene a function named main that returns a Pyramid WSGI application. This function is meant to be called by the PasteDeploy framework as a result of running pserve. Within this function, application conguration is performed. Line 7 creates an instance of a Congurator. Line 8 registers a static view, which will serve up the les from the myproject:static asset specication (the static directory of the myproject package). Line 9 adds a route to the conguration. This route is later used by a view in the views module. Line 10 calls config.scan(), which picks up view registrations declared elsewhere in the package (in this case, in the views.py module). Line 11 returns a WSGI application to the caller of the function (Pyramids pserve).
5.9.2 views.py
Much of the heavy lifting in a Pyramid application is done by view callables. A view callable is the main tool of a Pyramid web application developer; it is a bit of code which accepts a request and which returns a response.
1 2 3 4 5 6
59
5. CREATING A PYRAMID PROJECT Lines 4-6 dene and register a view callable named my_view. The function named my_view is decorated with a view_config decorator (which is processed by the config.scan() line in our __init__.py). The view_cong decorator asserts that this view be found when a route named home is matched. In our case, because our __init__.py maps the route named home to the URL pattern /, this route will match when a visitor visits the root URL. The view_cong decorator also names a renderer, which in this case is a template that will be used to render the result of the view callable. This particular view declaration points at templates/mytemplate.pt, which is a asset specication that species the mytemplate.pt le within the templates directory of the myproject package. The asset specication could have also been specied as myproject:templates/mytemplate.pt; the leading package name and colon is optional. The template le it actually points to is a Chameleon ZPT template le. This view callable function is handed a single piece of information: the request. The request is an instance of the WebOb Request class representing the browsers request to our server. This view returns a dictionary. When this view is invoked, a renderer converts the dictionary returned by the view into HTML, and returns the result as the response. This view is congured to invoke a renderer which uses a Chameleon ZPT template (templates/my_template.pt). See Writing View Callables Which Use a Renderer for more information about how views, renderers, and templates relate and cooperate. Because our development.ini has a pyramid.reload_templates = true directive indicating that templates should be reloaded when they change, you wont need to restart the application server to see changes you make to templates. During development, this is handy. If this directive had been false (or if the directive did not exist), you would need to restart the application server for each template change. For production applications, you should set your projects pyramid.reload_templates to false to increase the speed at which templates may be rendered.
5.9.3 static
This directory contains static assets which support the mytemplate.pt template. It includes CSS and images.
5.9.4 templates/mytemplate.pt
The single Chameleon template that exists in the project. Its contents are too long to show here, but it displays a default page when rendered. It is referenced by the call to @view_config as the renderer of the my_view view callable in the views.py le. See Writing View Callables Which Use a Renderer for more information about renderers. Templates are accessed and used by view congurations and sometimes by view functions themselves. See Using Templates Directly and Templates Used as Renderers via Conguration. 60
5.9.5 tests.py
The tests.py module includes unit tests for your application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
class ViewTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def test_my_view(self): from .views import my_view request = testing.DummyRequest() info = my_view(request) self.assertEqual(info[project], MyProject)
This sample tests.py le has a single unit test dened within it. This test is executed when you run python setup.py test. You may add more tests here as you build your application. You are not required to write tests to use Pyramid, this le is simply provided as convenience and example. See Unit, Integration, and Functional Testing for more information about writing Pyramid unit tests.
5. CREATING A PYRAMID PROJECT Create a views directory inside your myproject package directory (the same directory which holds views.py). Move the existing views.py le to a le inside the new views directory named, say, blog.py. Create a le within the new views directory named __init__.py (it can be empty, this just tells Python that the views directory is a package. You can then continue to add view callable functions to the blog.py module, but you can also add other .py les which contain view callable functions to the views directory. As long as you use the @view_config directive to register views in conjunction with config.scan() they will be picked up automatically when the application is restarted.
62
63
64
CHAPTER
SIX
STARTUP
When you cause a Pyramid application to start up in a console window, youll see something much like this show up on the console:
$ pserve myproject/MyProject.ini Starting server in PID 16601. serving on 0.0.0.0:6543 view at http://127.0.0.1:6543
This chapter explains what happens between the time you press the Return key on your keyboard after typing pserve myproject/MyProject.ini and the time the line serving on 0.0.0.0:6543 ... is output to your console.
6. STARTUP 2. The framework nds a section named either [app:main], [pipeline:main], or [composite:main] in the .ini le. This section represents the conguration of a WSGI application that will be served. If youre using a simple application (e.g. [app:main]), the applications paste.app_factory entry point will be named on the use= line within the sections conguration. If, instead of a simple application, youre using a WSGI pipeline (e.g. a [pipeline:main] section), the application named on the last element will refer to your Pyramid application. If instead of a simple application or a pipeline, youre using a composite (e.g. [composite:main]), refer to the documentation for that particular composite to understand how to make it refer to your Pyramid application. In most cases, a Pyramid application built from a scaffold will have a single [app:main] section in it, and this will be the application served. 3. The framework nds all logging related conguration in the .ini le and uses it to congure the Python standard library logging system for this application. See Logging Conguration for more information. 4. The applications constructor named by the entry point reference on the use= line of the section representing your Pyramid application is passed the key/value parameters mentioned within the section in which its dened. The constructor is meant to return a router instance, which is a WSGI application. For Pyramid applications, the constructor will be a function named main in the __init__.py le within the package in which your application lives. If this function succeeds, it will return a Pyramid router instance. Heres the contents of an example __init__.py module:
1 2 3 4 5 6 7 8 9 10 11
def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ 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()
Note that the constructor function accepts a global_config argument, which is a dictionary of key/value pairs mentioned in the [DEFAULT] section of an .ini le (if [DEFAULT] is present). It also accepts a **settings argument, which collects another set of arbitrary key/value pairs. The arbitrary key/value pairs received by this function in **settings will be composed of all the key/value pairs that are present in the [app:main] section (except for the use= setting) when this function is called by when you run pserve. Our generated development.ini le looks like so:
66
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
[app:main] use = egg:MyProject 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 [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 # Begin logging configuration [loggers] keys = root, myproject [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_myproject] level = DEBUG handlers = qualname = myproject [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 # End logging configuration
67
6. STARTUP In this case, the myproject.__init__:main function referred to by the entry point URI egg:MyProject (see development.ini for more information about entry point URIs, and how they relate to callables), will receive the key/value pairs {pyramid.reload_templates:true, pyramid.debug_authorization:false, pyramid.debug_notfound:false, pyramid.debug_routematch:false, pyramid.debug_templates:true, pyramid.default_locale_name:en}. See Environment Variables and .ini File Settings for the meanings of these keys. 5. The main function rst constructs a Configurator instance, passing the settings dictionary captured via the **settings kwarg as its settings argument. The settings dictionary contains all the options in the [app:main] section of our .ini le except the use option (which is internal to PasteDeploy) such as pyramid.reload_templates, pyramid.debug_authorization, etc. 6. The main function then calls various methods on the instance of the class Configurator created in the previous step. The intent of calling these methods is to populate an application registry, which represents the Pyramid conguration related to the application. 7. The make_wsgi_app() method is called. The result is a router instance. The router is associated with the application registry implied by the congurator previously populated by other methods run against the Congurator. The router is a WSGI application. 8. A ApplicationCreated event is emitted (see Using Events for more information about events). 9. Assuming there were no errors, the main function in myproject returns the router instance created by pyramid.config.Configurator.make_wsgi_app() back to pserve. As far as pserve is concerned, it is just another WSGI application. 10. pserve starts the WSGI server dened within the [server:main] section. In our case, this is the Waitress server (use = egg:waitress#main), and it will listen on all interfaces (host = 0.0.0.0), on port number 6543 (port = 6543). The server code itself is what prints serving on 0.0.0.0:6543 view at http://127.0.0.1:6543. The server serves the application, and the application is running, waiting to receive requests.
CHAPTER
SEVEN
REQUEST PROCESSING
Once a Pyramid application is up and running, it is ready to accept requests and return responses. What happens from the time a WSGI request enters a Pyramid application through to the point that Pyramid hands off a response back to WSGI for upstream processing? 1. A user initiates a request from his browser to the hostname and port number of the WSGI server used by the Pyramid application. 2. The WSGI server used by the Pyramid application passes the WSGI environment to the __call__ method of the Pyramid router object. 3. A request object is created based on the WSGI environment. 4. The application registry and the request object created in the last step are pushed on to the thread local stack that Pyramid uses to allow the functions named get_current_request() and get_current_registry() to work. 5. A NewRequest event is sent to any subscribers. 6. If any route has been dened within application conguration, the Pyramid router calls a URL dispatch route mapper. The job of the mapper is to examine the request to determine whether any user-dened route matches the current WSGI environment. The router passes the request as an argument to the mapper. 7. If any route matches, the route mapper adds attributes to the request: matchdict and matched_route attributes are added to the request object. The former contains a dictionary representing the matched dynamic elements of the requests PATH_INFO value, the latter contains the IRoute object representing the route which matched. The root object associated with the route found is also generated: if the route conguration which matched has an associated a factory argument, this factory is used to generate the root object, otherwise a default root factory is used. 69
7. REQUEST PROCESSING 8. If a route match was not found, and a root_factory argument was passed to the Congurator constructor, that callable is used to generate the root object. If the root_factory argument passed to the Congurator constructor was None, a default root factory is used to generate a root object. 9. The Pyramid router calls a traverser function with the root object and the request. The traverser function attempts to traverse the root object (using any existing __getitem__ on the root object and subobjects) to nd a context. If the root object has no __getitem__ method, the root itself is assumed to be the context. The exact traversal algorithm is described in Traversal. The traverser function returns a dictionary, which contains a context and a view name as well as other ancillary information. 10. The request is decorated with various names returned from the traverser (such as context, view_name, and so forth), so they can be accessed via e.g. request.context within view code. 11. A ContextFound event is sent to any subscribers. 12. Pyramid looks up a view callable using the context, the request, and the view name. If a view callable doesnt exist for this combination of objects (based on the type of the context, the type of the request, and the value of the view name, and any predicate attributes applied to the view conguration), Pyramid raises a HTTPNotFound exception, which is meant to be caught by a surrounding exception view. 13. If a view callable was found, Pyramid attempts to call it. If an authorization policy is in use, and the view conguration is protected by a permission, Pyramid determines whether the view callable being asked for can be executed by the requesting user based on credential information in the request and security information attached to the context. If the view execution is allowed, Pyramid calls the view callable to obtain a response. If view execution is forbidden, Pyramid raises a HTTPForbidden exception. 14. If any exception is raised within a root factory, by traversal, by a view callable or by Pyramid itself (such as when it raises HTTPNotFound or HTTPForbidden), the router catches the exception, and attaches it to the request as the exception attribute. It then attempts to nd a exception view for the exception that was caught. If it nds an exception view callable, that callable is called, and is presumed to generate a response. If an exception view that matches the exception cannot be found, the exception is reraised. 15. The following steps occur only when a response could be successfully generated by a normal view callable or an exception view callable. Pyramid will attempt to execute any response callback functions attached via add_response_callback(). A NewResponse event is then sent to any subscribers. The response objects __call__ method is then used to generate a WSGI response. The response is sent back to the upstream WSGI server. 70
16. Pyramid will attempt to execute add_finished_callback(). 17. The thread local stack is popped.
any
nished
callback
functions
attached
via
71
7. REQUEST PROCESSING
72
This is a very high-level overview that leaves out various details. For more detail about subsystems invoked by the Pyramid router such as traversal, URL dispatch, views, and event processing, see URL Dispatch, Views, and Using Events.
73
7. REQUEST PROCESSING
74
CHAPTER
EIGHT
URL DISPATCH
URL dispatch provides a simple way to map URLs to view code using a simple pattern matching language. An ordered set of patterns is checked one-by-one. If one of the patterns matches the path information associated with a request, a particular view callable is invoked. A view callable is a specic bit of code, dened in your application, that receives the request and returns a response object.
8. URL DISPATCH
When a view callable added to the conguration by way of add_view() becomes associated with a route via its route_name predicate, that view callable will always be found and invoked when the associated route pattern matches during a request. More commonly, you will not use any add_view statements in your projects setup code, instead only using add_route statements using a scan for to associate view callables with routes. For example, if this is a portion of your projects __init__.py:
# in your projects __init__.py (mypackage.__init__) config.add_route(myroute, /prefix/{one}/{two}) config.scan(mypackage)
Note that we dont call add_view() in this setup code. However, the above scan execution config.scan(mypackage) will pick up all conguration decoration, including any objects decorated with the pyramid.view.view_config decorator in the mypackage Python package. For example, if you have a views.py in your package, a scan will pick up any of its conguration decorators, so we can add one there that references myroute as a route_name parameter:
# in your projects views.py module (mypackage.views) from pyramid.view import view_config from pyramid.response import Response @view_config(route_name=myroute) def myview(request): return Response(OK)
The above combination of add_route and scan is completely equivalent to using the previous combination of add_route and add_view. 76
and:
/{foo}/bar/baz
A pattern segment (an individual item between / characters in the pattern) may either be a literal string (e.g. foo) or it may be a replacement marker (e.g. {foo}) or a certain combination of both. A replacement marker does not need to be preceded by a / character. A replacement marker is in the format {name}, where this means accept any characters up to the next slash character and use this as the name matchdict value. A replacement marker in a pattern must begin with an uppercase or lowercase ASCII letter or an underscore, and can be composed only of uppercase or lowercase ASCII letters, underscores, and numbers. For example: a, a_b, _b, and b9 are all valid replacement marker names, but 0a is not.
A replacement marker could not start with an underscore until Pyramid 1.2. Previous versions required that the replacement marker start with an uppercase or lowercase letter.
A matchdict is the dictionary representing the dynamic parts extracted from a URL based on the routing pattern. It is available as request.matchdict. For example, the following pattern denes one literal segment (foo) and two replacement markers (baz, and bar):
foo/{baz}/{bar}
The above pattern will match these URLs, generating the following matchdicts:
77
8. URL DISPATCH
foo/1/2 foo/abc/def
The match for a segment replacement marker in a segment will be done only up to the rst nonalphanumeric character in the segment in the pattern. So, for instance, if this route pattern was used:
foo/{name}.html
The literal path /foo/biz.html will match the above route pattern, and the match result will be {name:ubiz}. However, the literal path /foo/biz will not match, because it does not contain a literal .html at the end of the segment represented by {name}.html (it only contains biz, not biz.html). To capture both segments, two replacement markers can be used:
foo/{name}.{ext}
The literal path /foo/biz.html will match the above route pattern, and the match result will be {name: biz, ext: html}. This occurs because there is a literal part of . (period) between the two replacement markers {name} and {ext}. Replacement markers can optionally specify a regular expression which will be used to decide whether a path segment should match the marker. To specify that a replacement marker should match only a specic set of characters as dened by a regular expression, you must use a slightly extended form of replacement marker syntax. Within braces, the replacement marker name must be followed by a colon, then directly thereafter, the regular expression. The default regular expression associated with a replacement marker [^/]+ matches one or more characters which are not a slash. For example, under the hood, the replacement marker {foo} can more verbosely be spelled as {foo:[^/]+}. You can change this to be an arbitrary regular expression to match an arbitrary sequence of characters, such as {foo:\d+} to match only digits. It is possible to use two replacement markers without any literal characters between them, for instance /{foo}{bar}. However, this would be a nonsensical pattern without specifying a custom regular expression to restrict what each marker captures. Segments must contain at least one character in order to match a segment replacement marker. For example, for the URL /abc/: 78
8.2. ROUTE CONFIGURATION /abc/{foo} will not match. /{foo}/ will match. Note that values representing matched path segments will be url-unquoted and decoded from UTF-8 into Unicode within the matchdict. So for instance, the following pattern:
foo/{bar}
The matchdict will look like so (the value is URL-decoded / UTF-8 decoded):
{bar:uLa Pe\xf1a}
Literal strings in the path segment should represent the decoded value of the PATH_INFO provided to Pyramid. You dont want to use a URL-encoded value or a bytestring representing the literals UTF-8 in the pattern. For example, rather than this:
/Foo%20Bar/{baz}
For patterns that contain high-order characters in its literals, youll want to use a Unicode value as the pattern as opposed to any URL-encoded or UTF-8-encoded value. For example, you might be tempted to use a bytestring pattern like this:
/La Pe\xc3\xb1a/{x}
But this will either cause an error at startup time or it wont match properly. Youll want to use a Unicode value as the pattern instead rather than raw bytestring escapes. You can use a high-order Unicode value as the pattern by using Python source le encoding plus the real character in the Unicode pattern in the source, like so:
79
8. URL DISPATCH
/La Pea/{x}
Or you can ignore source le encoding and use equivalent Unicode escape characters in the pattern.
/La Pe\xf1a/{x}
Dynamic segment names cannot contain high-order characters, so this applies only to literals in the pattern. If the pattern has a * in it, the name which follows it is considered a remainder match. A remainder match must come at the end of the pattern. Unlike segment replacement markers, it does not need to be preceded by a slash. For example:
foo/{baz}/{bar}*fizzle
The above pattern will match these URLs, generating the following matchdicts:
foo/1/2/ -> {baz:u1, bar:u2, fizzle:()}
Note that when a *stararg remainder match is matched, the value put into the matchdict is turned into a tuple of path segments representing the remainder of the path. These path segments are url-unquoted and decoded from UTF-8 into Unicode. For example, for the following pattern:
foo/*fizzle
By default, the *stararg will parse the remainder sections into a tuple split by segment. Changing the regular expression used to match a marker can also capture the remainder of the URL, for example: 80
foo/{baz}/{bar}{fizzle:.*}
The above pattern will match these URLs, generating the following matchdicts:
foo/1/2/ foo/abc/def/a/b/c -> {baz:u1, bar:u2, fizzle:u} -> {baz:uabc, bar:udef, fizzle: ua/b/c}
This occurs because the default regular expression for a marker is [^/]+ which will match everything up to the rst /, while {fizzle:.*} will result in a regular expression match of .* capturing the remainder into a single value.
In such a conguration, the members/abc pattern would never be matched. This is because the match ordering will always match members/{def} rst; the route conguration with members/abc will never be evaluated.
81
8. URL DISPATCH
If no route URL pattern matches, the matchdict object attached to the request will be None.
If no route URL pattern matches, the matched_route object attached to the request will be None.
8.4.1 Example 1
The simplest route declaration which congures a route match to directly result in a particular view callable being invoked:
1 2
83
8. URL DISPATCH When a route conguration with a view attribute is added to the system, and an incoming request matches the pattern of the route conguration, the view callable named as the view attribute of the route conguration will be invoked. In the case of the above example, when the URL of a request matches /site/{id}, the view callable at the Python dotted path name mypackage.views.site_view will be called with the request. In other words, weve associated a view callable directly with a route pattern. When the /site/{id} route pattern matches during a request, the site_view view callable is invoked with that request as its sole argument. When this route matches, a matchdict will be generated and attached to the request as request.matchdict. If the specic URL matched is /site/1, the matchdict will be a dictionary with a single key, id; the value will be the string 1, ex.: {id:1}. The mypackage.views module referred to above might look like so:
1 2 3 4
The view has access to the matchdict directly via the request, and can access variables within it that match keys present as a result of the route pattern. See Views, and View Conguration for more information about views.
8.4.2 Example 2
Below is an example of a more complicated set of route statements you might add to your application:
1 2 3 4 5 6 7
config.add_route(idea, ideas/{idea}) config.add_route(user, users/{user}) config.add_route(tag, tags/{tag}) config.add_view(mypackage.views.idea_view, route_name=idea) config.add_view(mypackage.views.user_view, route_name=user) config.add_view(mypackage.views.tag_view, route_name=tag)
The above conguration will allow Pyramid to service URLs in these forms:
84
When a URL matches the pattern /ideas/{idea}, the view callable available at the dotted Python pathname mypackage.views.idea_view will be called. For the specic URL /ideas/1, the matchdict generated and attached to the request will consist of {idea:1}. When a URL matches the pattern /users/{user}, the view callable available at the dotted Python pathname mypackage.views.user_view will be called. For the specic URL /users/1, the matchdict generated and attached to the request will consist of {user:1}. When a URL matches the pattern /tags/{tag}, the view callable available at the dotted Python pathname mypackage.views.tag_view will be called. For the specic URL /tags/1, the matchdict generated and attached to the request will consist of {tag:1}. In this example weve again associated each of our routes with a view callable directly. In all cases, the request, which will have a matchdict attribute detailing the information found in the URL by the process will be passed to the view callable.
8.4.3 Example 3
The context resource object passed in to a view found as the result of URL dispatch will, by default, be an instance of the object returned by the root factory congured at startup time (the root_factory argument to the Congurator used to congure the application). You can override this behavior by passing in a factory argument to the add_route() method for a particular route. The factory should be a callable that accepts a request and returns an instance of a class that will be the context resource used by the view. An example of using a route with a factory:
1 2
The above route will manufacture an Idea resource as a context, assuming that mypackage.resources.Idea resolves to a class that accepts a request in its __init__. For example:
85
8. URL DISPATCH
1 2 3
In a more complicated application, this root factory might be a class representing a SQLAlchemy model. See Route Factories for more details about how to use route factories.
config.add_route(root, )
config.add_route(root, /)
This would return something like the string http://example.com/1/2/3 (at least if the current protocol and hostname implied http://example.com). To generate only the path portion of a URL from a route, pyramid.request.Request.route_path() API instead of route_url(). use the
86
This will return the string /1/2/3 rather than a full URL. Replacement values passed to route_url or route_path must be Unicode or bytestrings encoded in UTF-8. One exception to this rule exists: if youre trying to replace a remainder match value (a *stararg replacement value), the value may be a tuple containing Unicode strings or UTF-8 strings. Note that URLs and paths generated by route_path and route_url are always URL-quoted string types (they contain no non-ASCII characters). Therefore, if youve added a route like so:
config.add_route(la, u/La Pea/{city})
And you later generate a URL using route_path or route_url like so:
url = request.route_path(la, city=uQubec)
You will wind up with the path encoded to UTF-8 and URL quoted like so:
/La%20Pe%C3%B1a/Qu%C3%A9bec
And you later generate a URL using route_path or route_url using a string as the replacement value:
url = request.route_path(abc, foo=uQubec/biz)
The value you pass will be URL-quoted except for embedded slashes in the result:
/a/b/c/Qu%C3%A9bec/biz
You can get a similar result by passing a tuple composed of path elements:
87
8. URL DISPATCH
Each value in the tuple will be url-quoted and joined by slashes in this case:
/a/b/c/Qu%C3%A9bec/biz
Routes added with a True static keyword argument will never be considered for matching at request time. Static routes are useful for URL generation purposes only. As a result, it is usually nonsensical to provide other non-name and non-pattern arguments to add_route() when static is passed as True, as none of the other arguments will ever be employed. A single exception to this rule is use of the pregenerator argument, which is not ignored when static is True.
88
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from pyramid.httpexceptions import HTTPNotFound def notfound(request): return HTTPNotFound(Not found, bro.) def no_slash(request): return Response(No slash) def has_slash(request): return Response(Has slash) def main(g, **settings): config = Configurator() config.add_route(noslash, no_slash) config.add_route(hasslash, has_slash/) config.add_view(no_slash, route_name=noslash) config.add_view(has_slash, route_name=hasslash) config.add_notfound_view(notfound, append_slash=True)
If a request enters the application with the PATH_INFO value of /no_slash, the rst route will match and the browser will show No slash. However, if a request enters the application with the PATH_INFO value of /no_slash/, no route will match, and the slash-appending not found view will not nd a matching route with an appended slash. As a result, the notfound view will be called and it will return a Not found, bro. body. If a request enters the application with the PATH_INFO value of /has_slash/, the second route will match. If a request enters the application with the PATH_INFO value of /has_slash, a route will be found by the slash-appending not found view. An HTTP redirect to /has_slash/ will be returned to the users browser. As a result, the notfound view will never actually be called. The following application uses the pyramid.view.notfound_view_config pyramid.view.view_config decorators and a scan to do exactly the same job:
1 2 3 4 5 6 7 8 9 10 11
and
from pyramid.httpexceptions import HTTPNotFound from pyramid.view import notfound_view_config, view_config @notfound_view_config(append_slash=True) def notfound(request): return HTTPNotFound(Not found, bro.) @view_config(route_name=noslash) def no_slash(request): return Response(No slash)
89
8. URL DISPATCH
12 13 14 15 16 17 18 19 20
@view_config(route_name=hasslash) def has_slash(request): return Response(Has slash) def main(g, **settings): config = Configurator() config.add_route(noslash, no_slash) config.add_route(hasslash, has_slash/) config.scan()
You should not rely on this mechanism to redirect POST requests. The redirect of the slashappending not found view will turn a POST request into a GET, losing any POST data in the original request.
See pyramid.view and Changing the Not Found View for a more general description of how to congure a view and/or a not found view.
[chrism@thinko pylonsbasic]$ PYRAMID_DEBUG_ROUTEMATCH=true \ bin/pserve development.ini Starting server in PID 13586. serving on 0.0.0.0:6543 view at http://127.0.0.1:6543 2010-12-16 14:45:19,956 no route matched for url \ http://localhost:6543/wontmatch 2010-12-16 14:45:20,010 no route matched for url \ http://localhost:6543/favicon.ico 2010-12-16 14:41:52,084 route matched for url \ http://localhost:6543/static/logo.png; \ route_name: static/, ....
90
8.10. USING A ROUTE PREFIX TO COMPOSE APPLICATIONS See Environment Variables and .ini File Settings for more information about how, and where to set these values. You can also use the proutes command to see a display of all the routes congured in your application; for more information, see Displaying All Application Routes.
from pyramid.config import Configurator def users_include(config): config.add_route(show_users, /show) def main(global_config, **settings): config = Configurator() config.include(users_include, route_prefix=/users)
In the above conguration, the show_users route will have an effective route pattern of /users/show, instead of /show because the route_prefix argument will be prepended to the pattern. The route will then only match if the URL path is /users/show, and when the pyramid.request.Request.route_url() function is called with the route name show_users, it will generate a URL with that same path. Route prexes are recursive, so if a callable executed via an include itself turns around and includes another callable, the second-level route prex will be prepended with the rst: 91
8. URL DISPATCH
1 2 3 4 5 6 7 8 9 10 11 12
from pyramid.config import Configurator def timing_include(config): config.add_route(show_times, /times) def users_include(config): config.add_route(show_users, /show) config.include(timing_include, route_prefix=/timing) def main(global_config, **settings): config = Configurator() config.include(users_include, route_prefix=/users)
In the above conguration, the show_users route will still have an effective route pattern of /users/show. The show_times route however, will have an effective pattern of /users/timing/times. Route prexes have no impact on the requirement that the set of route names in any given Pyramid conguration must be entirely unique. If you compose your URL dispatch application out of many small subapplications using pyramid.config.Configurator.include(), its wise to use a dotted name for your route names, so theyll be unlikely to conict with other packages that may be added in the future. For example:
1 2 3 4 5 6 7 8 9 10 11 12
from pyramid.config import Configurator def timing_include(config): config.add_route(timing.show_times, /times) def users_include(config): config.add_route(users.show_users, /show) config.include(timing_include, route_prefix=/timing) def main(global_config, **settings): config = Configurator() config.include(users_include, route_prefix=/users)
8.11. CUSTOM ROUTE PREDICATES The info dictionary has a number of contained values: match is a dictionary: it represents the arguments matched in the URL by the route. route is an object representing the route which was matched (see pyramid.interfaces.IRoute for the API of such a route object). info[match] is useful when predicates need access to the route match. For example:
1 2 3 4 5 6 7 8 9 10
def any_of(segment_name, *allowed): def predicate(info, request): if info[match][segment_name] in allowed: return True return predicate num_one_two_or_three = any_of(num, one, two, three) config.add_route(route_to_num, /{num}, custom_predicates=(num_one_two_or_three,))
The above any_of function generates a predicate which ensures that the match value named segment_name is in the set of allowable values represented by allowed. We use this any_of function to generate a predicate function named num_one_two_or_three, which ensures that the num segment is one of the values one, two, or three , and use the result as a custom predicate by feeding it inside a tuple to the custom_predicates argument to add_route(). A custom route predicate may also modify the match dictionary. For instance, a predicate might do some type conversion of values:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
def integers(*segment_names): def predicate(info, request): match = info[match] for segment_name in segment_names: try: match[segment_name] = int(match[segment_name]) except (TypeError, ValueError): pass return True return predicate ymd_to_int = integers(year, month, day) config.add_route(ymd, /{year}/{month}/{day}, custom_predicates=(ymd_to_int,))
Note that a conversion predicate is still a predicate so it must return True or False; a predicate that does only conversion, such as the one we demonstrate above should unconditionally return True. 93
8. URL DISPATCH To avoid the try/except uncertainty, the route pattern can contain regular expressions specifying requirements for that marker. For instance:
1 2 3 4 5 6 7 8 9 10 11 12
def integers(*segment_names): def predicate(info, request): match = info[match] for segment_name in segment_names: match[segment_name] = int(match[segment_name]) return True return predicate ymd_to_int = integers(year, month, day) config.add_route(ymd, /{year:\d+}/{month:\d+}/{day:\d+}, custom_predicates=(ymd_to_int,))
Now the try/except is no longer needed because the route will not match at all unless these markers match \d+ which requires them to be valid digits for an int type conversion. The match dictionary passed within info to each predicate attached to a route will be the same dictionary. Therefore, when registering a custom predicate which modies the match dict, the code registering the predicate should usually arrange for the predicate to be the last custom predicate in the custom predicate list. Otherwise, custom predicates which re subsequent to the predicate which performs the match modication will receive the modied match dictionary.
It is a poor idea to rely on ordering of custom predicates to build a conversion pipeline, where one predicate depends on the side effect of another. For instance, its a poor idea to register two custom predicates, one which handles conversion of a value to an int, the next which handles conversion of that integer to some custom object. Just do all that in a single custom predicate. The route object in the info dict is an object that has two useful attributes: name and pattern. The name attribute is the route name. The pattern attribute is the route pattern. An example of using the route in a set of route predicates:
1 2 3 4 5 6 7 8
def twenty_ten(info, request): if info[route].name in (ymd, ym, y): return info[match][year] == 2010 config.add_route(y, /{year}, custom_predicates=(twenty_ten,)) config.add_route(ym, /{year}/{month}, custom_predicates=(twenty_ten,)) config.add_route(ymd, /{year}/{month}/{day}, custom_predicates=(twenty_ten,))
94
8.12. ROUTE FACTORIES The above predicate, when added to a number of route congurations ensures that the year match argument is 2010 if and only if the route name is ymd, ym, or y. You can also caption the predicates by setting the __text__ attribute. This will help you with the pviews command (see Displaying All Application Routes) and the pyramid_debugtoolbar. If a predicate is a class just add __text__ property in a standard manner.
1 2 3 4 5 6
class DummyCustomPredicate1(object): def __init__(self): self.__text__ = my custom class predicate class DummyCustomPredicate2(object): __text__ = my custom class predicate
If a predicate is a method youll need to assign it after method declaration (see PEP 232)
1 2 3
If a predicate is a classmethod using @classmethod will not work, but you can still easily do it by wrapping it in classmethod call.
1 2 3 4
Same will work with staticmethod, just use staticmethod instead of classmethod. See also pyramid.interfaces.IRoute for more API documentation about route objects.
95
8. URL DISPATCH
1 2 3
The factory can either be a Python object or a dotted Python name (a string) which points to such a Python object, as it is above. In this way, each route can use a different factory, making it possible to supply a different context resource object to the view related to each particular route. A factory must be a callable which accepts a request and returns an arbitrary Python object. For example, the below class can be used as a factory:
1 2 3
A route factory is actually conceptually identical to the root factory described at The Resource Tree. Supplying a different resource factory for each route is useful when youre trying to use a Pyramid authorization policy to provide declarative, context sensitive security checks; each resource can maintain a separate ACL, as documented in Using Pyramid Security With URL Dispatch. It is also useful when you wish to combine URL dispatch with traversal as documented within Combining Traversal and URL Dispatch.
class Article(object): def __init__(self, request): matchdict = request.matchdict article = matchdict.get(article, None) if article == 1: self.__acl__ = [ (Allow, editor, view) ]
96
8.14. ROUTE VIEW CALLABLE REGISTRATION AND LOOKUP DETAILS If the route archives/{article} is matched, and the article number is 1, Pyramid will generate an Article context resource with an ACL on it that allows the editor principal the view permission. Obviously you can do more generic things than inspect the routes match dict to see if the article argument matches a particular string; our sample Article factory class is not very ambitious.
See Security for more information about Pyramid security and ACLs.
8. URL DISPATCH
8.15 References
A tutorial showing how URL dispatch can be used to create a Pyramid application exists in SQLAlchemy + URL Dispatch Wiki Tutorial.
98
CHAPTER
NINE
VIEWS
One of the primary jobs of Pyramid is to nd and invoke a view callable when a request reaches your application. View callables are bits of code which do something interesting in response to a request made to your application. They are the meat of any interesting web application. A Pyramid view callable is often referred to in conversational shorthand as a view. In this documentation, however, we need to use less ambiguous terminology because there are signicant differences between view conguration, the code that implements a view callable, and the process of view lookup. This chapter describes how view callables should be dened. Well have to wait until a following chapter (entitled View Conguration) to nd out how we actually tell Pyramid to wire up view callables to particular URL patterns and other request circumstances.
9. VIEWS
from pyramid.response import Response class MyView(object): def __init__(self, request): self.request = request def __call__(self): return Response(hello)
The request object passed to __init__ is the same type of request object described in Dening a View Callable as a Function. If youd like to use a different attribute than __call__ to represent the method expected to return a response, you can use an attr value as part of the conguration for the view. See View Conguration Parameters. The same view callable class can be used in different view conguration statements with different attr values, each pointing at a different method of the class if youd like the class to represent a collection of related view callables. 100
Pyramid provides a range of different exception classes which inherit from pyramid.response.Response. For example, an instance of the class pyramid.httpexceptions.HTTPFound is also a valid response object because it inherits from Response. For examples, see HTTP Exceptions and Using a View Callable to Do an HTTP Redirect.
You can also return objects from view callables that arent instances of pyramid.response.Response in various circumstances. This can be helpful when writing tests and when attempting to share code between view callables. See Renderers for the common way to allow for this. A much less common way to allow for view callables to return non-Response objects is documented in Changing How Pyramid Treats View Responses.
101
9. VIEWS
An HTTP exception, instead of being raised, can alternately be returned (HTTP exceptions are also valid response objects):
1 2 3 4
A shortcut for creating an HTTP exception is the pyramid.httpexceptions.exception_response() function. This function accepts an HTTP status code and returns the corresponding HTTP exception. For example, instead of importing and constructing a HTTPUnauthorized response object, you can use the exception_response() function to construct and return the same object.
1 2 3 4
This is the case because 401 is the HTTP status code for HTTP Unauthorized. Therefore, raise exception_response(401) is functionally equivalent to raise HTTPUnauthorized(). Documentation which maps each HTTP response code to its purpose and its associated HTTP exception object is provided within pyramid.httpexceptions.
102
You can wire a view callable to be called whenever any of your other code raises a helloworld.exceptions.ValidationFailure exception:
1 2 3 4 5 6 7 8
from pyramid.view import view_config from helloworld.exceptions import ValidationFailure @view_config(context=ValidationFailure) def failed_validation(exc, request): response = Response(Failed validation: %s % exc.msg) response.status_int = 500 return response
103
9. VIEWS Assuming that a scan was run to pick up this view registration, this view callable will be invoked whenever a helloworld.exceptions.ValidationFailure is raised by your applications view code. The same exception raised by a custom root factory, a custom traverser, or a custom view or route predicate is also caught and hooked. Other normal view predicates can also be used in combination with an exception view registration:
1 2 3 4 5 6 7 8
from pyramid.view import view_config from helloworld.exceptions import ValidationFailure @view_config(context=ValidationFailure, route_name=home) def failed_validation(exc, request): response = Response(Failed validation: %s % exc.msg) response.status_int = 500 return response
The above exception view names the route_name of home, meaning that it will only be called when the route matched has a name of home. You can therefore have more than one exception view for any given exception in the system: the most specic one will be called when the set of request circumstances match the view registration. The only view predicate that cannot be used successfully when creating an exception view conguration is name. The name used to look up an exception view is always the empty string. Views registered as exception views which have a name will be ignored.
Normal (i.e., non-exception) views registered against a context resource type which inherits from Exception will work normally. When an exception view conguration is processed, two views are registered. One as a normal view, the other as an exception view. This means that you can use an exception as context for a normal view.
Exception views can be congured with any view registration mechanism: @view_config decorator or imperative add_view styles.
Pyramids exception view handling logic is implemented as a tween factory function: pyramid.tweens.excview_tween_factory(). If Pyramid exception view handling is desired, and tween factories are specied via the pyramid.tweens conguration setting, the pyramid.tweens.excview_tween_factory() function must be added to the pyramid.tweens conguration setting list explicitly. If it is not present, Pyramid will not perform exception view handling.
104
When the instance is raised, it is caught by the default exception response handler and turned into a response.
9.8 Handling Form Submissions in View Callables (Unicode and Character Set Issues)
Most web applications need to accept form submissions from web browsers and various other clients. In Pyramid, form submission handling logic is always part of a view. For a general overview of how to handle form submission data using the WebOb API, see Request and Response Objects and Query and POST variables within the WebOb documentation. Pyramid defers to WebOb for its request and response implementations, and handling form submission data is a property of the request implementation. Understanding WebObs request API is the key to understanding how to process form submission data. There are some defaults that you need to be aware of when trying to handle form submission data in a Pyramid view. Having high-order (i.e., non-ASCII) characters in data contained within form submissions is exceedingly common, and the UTF-8 encoding is the most common encoding used on the web for character data. Since Unicode values are much saner than working with and storing bytestrings, Pyramid congures the WebOb request machinery to attempt to decode form submission values into Unicode from UTF-8 implicitly. This implicit decoding happens when view code obtains form eld values via the 105
9. VIEWS request.params, request.GET, or request.POST APIs (see pyramid.request for details about these APIs).
Many people nd the difference between Unicode and UTF-8 confusing. Unicode is a standard for representing text that supports most of the worlds writing systems. However, there are many ways that Unicode data can be encoded into bytes for transit and storage. UTF-8 is a specic encoding for Unicode, that is backwards-compatible with ASCII. This makes UTF-8 very convenient for encoding data where a large subset of that data is ASCII characters, which is largely true on the web. UTF-8 is also the standard character encoding for URLs.
As an example, lets assume that the following form page is served up to a browser client, and its action points at some Pyramid view code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> </head> <form method="POST" action="myview"> <div> <input type="text" name="firstname"/> </div> <div> <input type="text" name="lastname"/> </div> <input type="submit" value="Submit"/> </form> </html>
The myview view code in the Pyramid application must expect that the values returned by request.params will be of type unicode, as opposed to type str. The following will work to accept a form post from the above form:
1 2 3
But the following myview view code may not work, as it tries to decode already-decoded (unicode) values obtained from request.params:
106
1 2 3 4 5
def myview(request): # the .decode(utf-8) will break below if there are any high-order # characters in the firstname or lastname firstname = request.params[firstname].decode(utf-8) lastname = request.params[lastname].decode(utf-8)
For implicit decoding to work reliably, you should ensure that every form you render that posts to a Pyramid view explicitly denes a charset encoding of UTF-8. This can be done via a response that has a ;charset=UTF-8 in its Content-Type header; or, as in the form above, with a meta http-equiv tag that implies that the charset is UTF-8 within the HTML head of the page containing the form. This must be done explicitly because all known browser clients assume that they should encode form data in the same character set implied by Content-Type value of the response containing the form when subsequently submitting that form. There is no other generally accepted way to tell browser clients which charset to use to encode form data. If you do not specify an encoding explicitly, the browser client will choose to encode form data in its default character set before submitting it, which may not be UTF-8 as the server expects. If a request containing form data encoded in a non-UTF8 charset is handled by your view code, eventually the request code accessed within your view will throw an error when it cant decode some high-order character encoded in another character set within form data, e.g., when request.params[somename] is accessed. If you are using the Response class to generate a response, or if you use the render_template_* templating APIs, the UTF-8 charset is set automatically as the default via the Content-Type header. If you return a Content-Type header without an explicit charset, a request will add a ;charset=utf-8 trailer to the Content-Type header value for you, for response content types that are textual (e.g. text/html, application/xml, etc) as it is rendered. If you are using your own response object, you will need to ensure you do this yourself. Only the values of request params obtained via request.params, request.GET or request.POST are decoded to Unicode objects implicitly in the Pyramid default conguration. The keys are still (byte) strings.
9. VIEWS The resource object found via tree traversal or URL dispatch. request A Pyramid Request object representing the current WSGI request. The following types work as view callables in this style: 1. Functions that accept two arguments: context, and request, e.g.:
1 2 3 4
2. Classes that have an __init__ method that accepts context, request and a __call__ method which accepts no arguments, e.g.:
1 2 3 4 5 6 7 8 9
from pyramid.response import Response class view(object): def __init__(self, context, request): self.context = context self.request = request def __call__(self): return Response(OK)
3. Arbitrary callables that have a __call__ method that accepts context, request, e.g.:
1 2 3 4 5 6
from pyramid.response import Response class View(object): def __call__(self, context, request): return Response(OK) view = View() # this is the view callable
This style of calling convention is most useful for traversal based applications, where the context object is frequently used within the view callable code itself. No matter which view calling convention is used, the view code always has access to the context via request.context.
108
109
9. VIEWS
110
CHAPTER
TEN
RENDERERS
A view callable neednt always return a Response object. If a view happens to return something which does not implement the Pyramid Response interface, Pyramid will attempt to use a renderer to construct a response. For example:
1 2 3 4 5
The above example returns a dictionary from the view callable. A dictionary does not implement the Pyramid response interface, so you might believe that this example would fail. However, since a renderer is associated with the view callable through its view conguration (in this case, using a renderer argument passed to view_config()), if the view does not return a Response object, the renderer will attempt to convert the result of the view to a response on the developers behalf. Of course, if no renderer is associated with a views conguration, returning anything except an object which implements the Response interface will result in an error. And, if a renderer is used, whatever is returned by the view must be compatible with the particular kind of renderer used, or an error may occur during view invocation. One exception exists: it is always OK to return a Response object, even when a renderer is congured. If a view callable returns a response object from a view that is congured with a renderer, the renderer is bypassed entirely. Various types of renderers exist, including serialization renderers and renderers which use templating systems. See also Writing View Callables Which Use a Renderer. 111
10. RENDERERS
config.add_view(myproject.views.my_view, renderer=json)
When this conguration is added to an application, the myproject.views.my_view view callable will now use a json renderer, which renders view return values to a JSON response serialization. Other built-in renderers include renderers which use the Chameleon templating language to render a dictionary to a response. Additional renderers can be added by developers to the system as necessary (see Adding and Changing Renderers). Views which use a renderer and return a non-Response value can vary non-body response attributes (such as headers and the HTTP status code) by attaching a property to the request.response attribute See Varying Attributes of Rendered Responses. If the view callable associated with a view conguration returns a Response object directly, any renderer associated with the view conguration is ignored, and the response is passed back to Pyramid unchanged. For example, if your view callable returns an instance of the pyramid.response.Response class as a response, no renderer will be employed.
1 2 3 4 5 6
from pyramid.response import Response from pyramid.view import view_config @view_config(renderer=json) def view(request): return Response(OK) # json renderer avoided
from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config @view_config(renderer=json) def view(request): return HTTPFound(location=http://example.com) # json renderer avoided
112
10.2. BUILT-IN RENDERERS You can of course also return the request.response attribute instead to avoid rendering:
1 2 3 4 5 6
from pyramid.view import view_config @view_config(renderer=json) def view(request): request.response.body = OK return request.response # json renderer avoided
The body of the response returned by such a view will be a string representing the str() serialization of the return value:
1
{content: Hello!}
Views which use the string renderer can vary non-body response attributes by using the API of the request.response attribute. See Varying Attributes of Rendered Responses. 113
10. RENDERERS
The body of the response returned by such a view will be a string representing the JSON serialization of the return value:
1
{"content": "Hello!"}
The return value neednt be a dictionary, but the return value must contain values serializable by the congured serializer (by default json.dumps).
Extra arguments can be passed to the serializer by overriding the default json renderer. See pyramid.renderers.JSON and Adding and Changing Renderers for more information.
You can congure a view to use the JSON renderer by naming json as the renderer argument of a view conguration, e.g. by using add_view():
1 2 3 4
Views which use the JSON renderer can vary non-body response attributes by using the api of the request.response attribute. See Varying Attributes of Rendered Responses.
114
10.2. BUILT-IN RENDERERS Serializing Custom Objects Custom objects can be made easily JSON-serializable in Pyramid by dening a __json__ method on the objects class. This method should return values natively JSON-serializable (such as ints, lists, dictionaries, strings, and so forth). It should accept a single additional argument, request, which will be the active request object at render time.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
from pyramid.view import view_config class MyObject(object): def __init__(self, x): self.x = x def __json__(self, request): return {x:self.x} @view_config(renderer=json) def objects(request): return [MyObject(1), MyObject(2)] # the JSON value returned by objects will be: # [{"x": 1}, {"x": 2}]
If you arent the author of the objects being serialized, it wont be possible (or at least not reasonable) to add a custom __json__ method to their classes in order to inuence serialization. If the object passed to the renderer is not a serializable type, and has no __json__ method, usually a TypeError will be raised during serialization. You can change this behavior by creating a custom JSON renderer and adding adapters to handle custom types. The renderer will attempt to adapt non-serializable objects using the registered adapters. A short example follows:
1 2 3 4 5 6 7 8 9 10
from pyramid.renderers import JSON json_renderer = JSON() def datetime_adapter(obj, request): return obj.isoformat() json_renderer.add_adapter(datetime.datetime, datetime_adapter) # then during configuration .... config = Configurator() config.add_renderer(json, json_renderer)
The adapter should accept two arguments: the object needing to be serialized and request, which will be the current request object at render time. The adapter should raise a TypeError if it cant determine what to do with the object. 115
10. RENDERERS See pyramid.renderers.JSON and Adding and Changing Renderers for more information.
pyramid.renderers.JSONP is a JSONP renderer factory helper which implements a hybrid json/jsonp renderer. JSONP is useful for making cross-domain AJAX requests. Unlike other renderers, a JSONP renderer needs to be congured at startup time by hand. Congure a JSONP renderer using the pyramid.config.Configurator.add_renderer() method:
from pyramid.config import Configurator config = Configurator() config.add_renderer(jsonp, JSONP(param_name=callback))
Once this renderer is registered via add_renderer() as above, can use jsonp as the renderer= parameter to @view_config pyramid.config.Configurator.add_view():
from pyramid.view import view_config @view_config(renderer=jsonp) def myview(request): return {greeting:Hello world}
you or
When a view is called that uses a JSONP renderer: If there is a parameter in the requests HTTP query string (aka request.GET) that matches the param_name of the registered JSONP renderer (by default, callback), the renderer will return a JSONP response. If there is no callback parameter in the requests query string, the renderer will return a plain JSON response. 116
10.2. BUILT-IN RENDERERS Javscript library AJAX functionality will help you make JSONP requests. For example, JQuery has a getJSON function, and has equivalent (but more complicated) functionality in its ajax function. For example (Javascript):
var api_url = http://api.geonames.org/timezoneJSON + ?lat=38.301733840000004 + &lng=-77.45869621 + &username=fred + &callback=?; jqhxr = $.getJSON(api_url);
The string callback=? above in the url param to the JQuery getAjax function indicates to jQuery that the query should be made as a JSONP request; the callback parameter will be automatically lled in for you and used. The same custom-object serialization scheme dened used for a normal JSON renderer in Serializing Custom Objects can be used when passing values to a JSONP renderer too.
10. RENDERERS automatically inserted into the set of keyword arguments passed to the template as the view keyword. If the view callable was a class, the view keyword will be an instance of that class. Also inserted into the keywords passed to the template are renderer_name (the string used in the renderer attribute of the directive), renderer_info (an object containing renderer-related information), context (the context resource of the view used to render the template), and request (the request passed to the view used to render the template). request is also available as req in Pyramid 1.3+. Heres an example view conguration which uses a Chameleon ZPT renderer:
1 2 3 4 5 6
Views which use a Chameleon renderer can vary response attributes by using the API of the request.response attribute. See Varying Attributes of Rendered Responses.
118
1 2 3 4 5 6
Its important to note that in Makos case, the relative path name foo.mak above is not relative to the package, but is relative to the directory (or directories) congured for Mako via the mako.directories conguration le setting. The renderer can also be provided in asset specication format. Heres an example view conguration which uses one:
1 2 3 4
The above conguration will use the le named foo.mak in the templates directory of the mypackage package. The Mako template renderer can take additional arguments beyond the standard pyramid.reload_templates setting, see the Environment Variables and .ini File Settings for additional Mako Template Render Settings.
119
10. RENDERERS
1 2 3 4 5 6
from pyramid.view import view_config @view_config(name=gone, renderer=templates/gone.pt) def myview(request): request.response.status = 404 Not Found return {URL:request.URL}
Note that mutations of request.response in views which return a Response object directly will have no effect unless the response object returned is request.response. For example, the following example calls request.response.set_cookie, but this call will have no effect, because a different Response object is returned.
1 2 3 4 5
from pyramid.response import Response def view(request): request.response.set_cookie(abc, 123) # this has no effect return Response(OK) # because were returning a different response
If you mutate request.response and youd like the mutations to have an effect, you must return request.response:
1 2 3
For more information on attributes of the request, see the API documentation in pyramid.request. For more information on the API of request.response, see pyramid.request.Request.response.
10.5. ADDING AND CHANGING RENDERERS response_content_type Denes the content-type of the resulting response, e.g. text/xml. response_headerlist A sequence of tuples describing header values that should be set in the response, e.g. [(Set-Cookie, abc=123), (X-My-Header, foo)]. response_status A WSGI-style status code (e.g. 200 OK) describing the status of the response. response_charset The character set (e.g. UTF-8) of the response. response_cache_for A value in seconds which will inuence Cache-Control and Expires headers in the returned response. The same can also be achieved by returning various values in the response_headerlist, this is purely a convenience.
config.add_renderer(.jinja2, mypackage.MyJinja2Renderer)
The rst argument is the renderer name. The second argument is a reference to an implementation of a renderer factory or a dotted Python name referring to such an object.
121
10. RENDERERS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
class RendererFactory: def __init__(self, info): """ Constructor: info will be an object having the following attributes: name (the renderer name), package (the package that was current at the time the renderer was registered), type (the renderer type name), registry (the current application registry) and settings (the deployment settings dictionary). """ def __call__(self, value, system): """ Call the renderer implementation with the value and the system value passed in as arguments and return the result (a string or unicode object). The value is the return value of a view. The system value is a dictionary containing available system values (e.g. view, context, and request). """
The formal interface denition of the info object passed to a renderer factory constructor is available as pyramid.interfaces.IRendererInfo. There are essentially two different kinds of renderer factories: A renderer factory which expects to accept an asset specication, or an absolute path, as the name attribute of the info object fed to its constructor. These renderer factories are registered with a name value that begins with a dot (.). These types of renderer factories usually relate to a le on the lesystem, such as a template. A renderer factory which expects to accept a token that does not represent a lesystem path or an asset specication in the name attribute of the info object fed to its constructor. These renderer factories are registered with a name value that does not begin with a dot. These renderer factories are typically object serializers.
Asset Specications An asset specication is a colon-delimited identier for an asset. The colon separates a Python package name from a package subpath. For example, the asset specication my.package:static/baz.css identies the le named baz.css in the static subdirectory of the my.package Python package.
122
1 2 3
Adding the above code to your application startup conguration will allow you to use the my.package.MyAMFRenderer renderer factory implementation in view congurations. Your application can use this renderer by specifying amf in the renderer attribute of a view conguration:
1 2 3 4 5
At startup time, when a view conguration is encountered, which has a name attribute that does not contain a dot, the full name value is used to construct a renderer from the associated renderer factory. In this case, the view conguration will create an instance of an MyAMFRenderer for each view conguration which includes amf as its renderer value. The name passed to the MyAMFRenderer constructor will always be amf. Heres an example of the registration of a more complicated renderer factory, which expects to be passed a lesystem path:
1 2
config.add_renderer(name=.jinja2, factory=my.package.MyJinja2Renderer)
Adding the above code to your application startup will allow you to use the my.package.MyJinja2Renderer renderer factory implementation in view congurations by referring to any renderer which ends in .jinja in the renderer attribute of a view conguration:
1 2 3 4 5
When a view conguration is encountered at startup time, which has a name attribute that does contain a dot, the value of the name attribute is split on its nal dot. The second element of the split is typically the lename extension. This extension is used to look up a renderer factory for the congured view. Then the value of renderer is passed to the factory to create a renderer for the view. In this case, the view conguration will create an instance of a MyJinja2Renderer for each view conguration which includes anything ending with .jinja2 in its renderer value. The name passed to the MyJinja2Renderer constructor will be the full value that was set as renderer= in the view conguration. 123
10. RENDERERS
config.add_renderer(.zpt, pyramid.chameleon_zpt.renderer_factory)
After you do this, Pyramid will treat templates ending in both the .pt and .zpt lename extensions as Chameleon ZPT templates. To change the default mapping in which les with a .pt extension are rendered via a Chameleon ZPT page template renderer, use a variation on the following in your applications startup code:
1
config.add_renderer(.pt, mypackage.pt_renderer)
After you do this, the renderer factory in mypackage.pt_renderer will be used to render templates which end in .pt, replacing the default Chameleon ZPT renderer. To associate a default renderer with all view congurations (even ones which do not possess a renderer attribute), pass None as the name attribute to the renderer tag:
1
config.add_renderer(None, mypackage.json_renderer_factory)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
from pyramid.event import subscriber from pyramid.event import NewRequest @subscriber(NewRequest) def set_xmlrpc_params(event): request = event.request if (request.content_type == text/xml and request.method == POST and not soapaction in request.headers and not x-pyramid-avoid-xmlrpc in request.headers): params, method = parse_xmlrpc_request(request) request.xmlrpc_params, request.xmlrpc_method = params, method request.is_xmlrpc = True request.override_renderer = xmlrpc return True
The result of such a subscriber will be to replace any existing static renderer congured by the developer with a (notional, nonexistent) XML-RPC renderer if the request appears to come from an XML-RPC client.
125
10. RENDERERS
126
CHAPTER
ELEVEN
TEMPLATES
A template is a le on disk which can be used to render dynamic data provided by a view. Pyramid offers a number of ways to perform templating tasks out of the box, and provides add-on templating support through a set of bindings packages. Out of the box, Pyramid provides templating via the Chameleon and Mako templating libraries. Chameleon provides support for two different types of templates: ZPT templates, and text templates. Before discussing how built-in templates are used in detail, well discuss two ways to render templates within Pyramid in general: directly, and via renderer conguration.
from pyramid.renderers import render_to_response def sample_view(request): return render_to_response(templates/foo.pt, {foo:1, bar:2}, request=request)
127
11. TEMPLATES The sample_view view callable function above returns a response object which contains the body of the templates/foo.pt template. In this case, the templates directory should live in the same directory as the module containing the sample_view function. The template author will have the names foo and bar available as top-level names for replacement or comparison purposes. In the example above, the path templates/foo.pt is relative to the directory containing the le which denes the view conguration. In this case, this is the directory containing the le that denes the sample_view function. Although a renderer path is usually just a simple relative pathname, a path named as a renderer can be absolute, starting with a slash on UNIX or a drive letter prex on Windows.
Only Chameleon templates support dening a renderer for a template relative to the location of the module where the view callable is dened. Mako templates, and other templating system bindings work differently. In particular, Mako templates use a lookup path as dened by the mako.directories conguration le instead of treating relative paths as relative to the current view module. See Templating With Mako Templates.
The path can alternately be a asset specication in the form some.dotted.package_name:relative/path. This makes it possible to address template assets which live in another package. For example:
1 2 3 4 5 6
from pyramid.renderers import render_to_response def sample_view(request): return render_to_response(mypackage:templates/foo.pt, {foo:1, bar:2}, request=request)
An asset specication points at a le within a Python package. In this case, it points at a le named foo.pt within the templates directory of the mypackage package. Using a asset specication instead of a relative template name is usually a good idea, because calls to render_to_response using asset specications will continue to work properly if you move the code containing them around.
Mako templating system bindings also respect absolute asset specications as an argument to any of the render* commands. If a template name denes a : (colon) character and is not an absolute path, it is treated as an absolute asset specication.
In the examples above we pass in a keyword argument named request representing the current Pyramid request. Passing a request keyword argument will cause the render_to_response function to supply the renderer with more correct system values (see System Values Used During Rendering), because most 128
11.1. USING TEMPLATES DIRECTLY of the information required to compose proper system values is present in the request. If your template relies on the name request or context, or if youve congured special renderer globals, make sure to pass request as a keyword argument in every call to a pyramid.renderers.render_* function. Every view must return a response object, except for views which use a renderer named via view conguration (which well see shortly). The pyramid.renderers.render_to_response() function is a shortcut function that actually returns a response object. This allows the example view above to simply return the result of its call to render_to_response() directly. Obviously not all APIs you might call to get response data will return a response object. For example, you might render one or more templates to a string that you want to use as response data. The pyramid.renderers.render() API renders a template to a string. We can manufacture a response object directly, and use that string as the body of the response:
1 2 3 4 5 6 7 8 9
from pyramid.renderers import render from pyramid.response import Response def sample_view(request): result = render(mypackage:templates/foo.pt, {foo:1, bar:2}, request=request) response = Response(result) return response
Because view callable functions are typically the only code in Pyramid that need to know anything about templates, and because view functions are very simple Python, you can use whatever templating system youre most comfortable with within Pyramid. Install the templating system, import its API functions into your views module, use those APIs to generate a string, then return that string as the body of a Pyramid Response object. For example, heres an example of using raw Mako from within a Pyramid view:
1 2 3 4 5 6 7 8
from mako.template import Template from pyramid.response import Response def make_view(request): template = Template(filename=/templates/template.mak) result = template.render(name=request.params[name]) response = Response(result) return response
You probably wouldnt use this particular snippet in a project, because its easier to use the Mako renderer bindings which already exist in Pyramid. But if your favorite templating system is not supported as a renderer extension for Pyramid, you can create your own simple combination as shown above. 129
11. TEMPLATES
If you use third-party templating languages without cooperating Pyramid bindings directly within view callables, the auto-template-reload strategy explained in Automatically Reloading Templates will not be available, nor will the template asset overriding capability explained in Overriding Assets be available, nor will it be possible to use any template using that language as a renderer. However, its reasonably easy to write custom templating system binding packages for use under Pyramid so that templates written in the language can be used as renderers. See Adding and Changing Renderers for instructions on how to create your own template renderer and Available Add-On Template System Bindings for example packages. If you need more control over the status code and content-type, or other response attributes from views that use direct templating, you may set attributes on the response that inuence these values. Heres an example of changing the content-type and status of the response object returned by render_to_response():
1 2 3 4 5 6 7 8 9
from pyramid.renderers import render_to_response def sample_view(request): response = render_to_response(templates/foo.pt, {foo:1, bar:2}, request=request) response.content_type = text/plain response.status_int = 204 return response
Heres an example of manufacturing a response object using the result of render() (a string):
1 2 3 4 5 6 7 8 9 10
from pyramid.renderers import render from pyramid.response import Response def sample_view(request): result = render(mypackage:templates/foo.pt, {foo:1, bar:2}, request=request) response = Response(result) response.content_type = text/plain return response
11.3. TEMPLATES USED AS RENDERERS VIA CONFIGURATION senting the template will be provided with a number of system values. These values are provided to the template: request The value provided as the request keyword argument to render_to_response or render or the request object passed to the view when the renderer= argument to view conguration is being used to render the template. req An alias for request. context The current Pyramid context if request was provided as a keyword argument to render_to_response or render, or None if the request keyword argument was not provided. This value will always be provided if the template is rendered as the result of a renderer= argument to view conguration being used. renderer_name The renderer name mypackage:templates/foo.pt. used to perform the rendering, e.g.
renderer_info An object implementing the pyramid.interfaces.IRendererInfo interface. Basically, an object with the following attributes: name, package and type. view The view callable object that was used to render this template. If the view callable is a method of a class-based view, this will be an instance of the class that the method was dened on. If the view callable is a function or instance, it will be that function or instance. Note that this value will only be automatically present when a template is rendered as a result of a renderer= argument; it will be None when the render_to_response or render APIs are used. You can dene more values which will be passed to every template executed as a result of rendering by dening renderer globals. What any particular renderer does with these system values is up to the renderer itself, but most template renderers, including Chameleon and Mako renderers, make these names available as top-level template variables.
11. TEMPLATES view callable. The dictionary items returned by the view callable will be made available to the renderer template as top-level names. The association of a template as a renderer for a view conguration makes it possible to replace code within a view callable that handles the rendering of a template. Heres an example of using a view_config decorator to specify a view conguration that names a template renderer:
1 2 3 4 5
from pyramid.view import view_config @view_config(renderer=templates/foo.pt) def my_view(request): return {foo:1, bar:2}
You do not need to supply the request value as a key in the dictionary result returned from a renderer-congured view callable. Pyramid automatically supplies this value for you so that the most correct system values are provided to the renderer.
The renderer argument to the @view_config conguration decorator shown above is the template path. In the example above, the path templates/foo.pt is relative. Relative to what, you ask? Because were using a Chameleon renderer, it means relative to the directory in which the le which denes the view conguration lives. In this case, this is the directory containing the le that denes the my_view function. View-conguration-relative asset specications work only in Chameleon, not in Mako templates.
Similar renderer conguration can be done imperatively. See Writing View Callables Which Use a Renderer. See also Built-In Renderers. Although a renderer path is usually just a simple relative pathname, a path named as a renderer can be absolute, starting with a slash on UNIX or a drive letter prex on Windows. The path can alternately be an asset specication in the form some.dotted.package_name:relative/path, making it possible to address template assets which live in another package. Not just any template from any arbitrary templating system may be used as a renderer. Bindings must exist specically for Pyramid to use a templating language template as a renderer. Currently, Pyramid has built-in support for two Chameleon templating languages: ZPT and text, and the Mako templating system. See Built-In Renderers for a discussion of their details. Pyramid also supports the use of Jinja2 templates as renderers. See Available Add-On Template System Bindings. 132
Why Use A Renderer via View Conguration Using a renderer in view conguration is usually a better way to render templates than using any rendering API directly from within a view callable because it makes the view callable more unit-testable. Views which use templating or rendering APIs directly must return a Response object. Making testing assertions about response objects is typically an indirect process, because it means that your test code often needs to somehow parse information out of the response body (often HTML). View callables congured with renderers externally via view conguration typically return a dictionary, as above. Making assertions about results returned in a dictionary is almost always more direct and straightforward than needing to parse HTML. By default, views rendered via a template renderer return a Response object which has a status code of 200 OK, and a content-type of text/html. To vary attributes of the response of a view that uses a renderer, such as the content-type, headers, or status attributes, you must use the API of the pyramid.response.Response object exposed as request.response within the view before returning the dictionary. See Varying Attributes of Rendered Responses for more information. The same set of system values are provided to templates rendered via a renderer view conguration as those provided to templates rendered imperatively. See System Values Used During Rendering.
from pyramid.view import view_config @view_config(renderer=templates/foo.pt) def my_view(request): return {foo:1, bar:2}
See also Built-In Renderers for more general information about renderers, including Chameleon ZPT renderers. 133
11. TEMPLATES
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>${project} Application</title> </head> <body> <h1 class="title">Welcome to <code>${project}</code>, an application generated by the <a href="http://docs.pylonsproject.org/projects/pyramid/current/" >pyramid</a> web application framework.</h1> </body> </html>
Note the use of Genshi -style ${replacements} above. This is one of the ways that Chameleon ZPT differs from standard ZPT. The above template expects to nd a project key in the set of keywords passed in to it via render() or render_to_response(). Typical ZPT attribute-based syntax (e.g. tal:content and tal:replace) also works in these templates.
134
5 6 7
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal"> <span metal:define-macro="hello"> <h1> Hello <span metal:define-slot="name">Fred</span>! </h1> </span> </html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal"> <span metal:use-macro="main.macros[hello]"> <span metal:fill-slot="name">Chris</span> </span> </html>
from pyramid.view import view_config @view_config(renderer=foo#bar.pt) def my_view(request): return {project:my project}
135
11. TEMPLATES The above will render only the bar macro dened within the foo.pt template instead of the entire template.
Then in your projects views.py module, you can create a view which renders this template:
1 2 3 4 5
See also Built-In Renderers for more general information about renderers, including Chameleon text renderers.
136
Note that I always name my Chameleon ZPT template les with a .pt extension and my Chameleon text template les with a .txt extension so that these svn:ignore patterns work.
The output tells you which template the error occurred in, as well as displaying the arguments passed to the template itself.
137
11. TEMPLATES
from pyramid.view import view_config @view_config(renderer=foo.mak) def my_view(request): return {project:my project}
For the above view callable to work, the following setting needs to be present in the application stanza of your congurations ini le:
mako.directories = mypackage:templates
This lets the Mako templating system know that it should look for templates in the templates subdirectory of the mypackage Python package. See Mako Template Render Settings for more information about the mako.directories setting and other Mako-related settings that can be placed into the applications ini le.
138
1 2 3 4 5 6 7 8 9 10 11
<html> <head> <title>${project} Application</title> </head> <body> <h1 class="title">Welcome to <code>${project}</code>, an application generated by the <a href="http://docs.pylonsproject.org/projects/pyramid/current/" >pyramid</a> web application framework.</h1> </body> </html>
This template doesnt use any advanced features of Mako, only the ${} replacement syntax for names that are passed in as renderer globals. See the the Mako documentation to use more advanced features.
from pyramid.view import view_config @view_config(renderer=foo#bar.mak) def my_view(request): return {project:my project}
The above will render the bar def from within the foo.mak template instead of the entire template.
11. TEMPLATES
Auto-template-reload behavior is not recommended for production sites as it slows rendering slightly; its usually only desirable during development.
In order to turn on automatic reloading of templates, you can use an environment variable, or a conguration le setting. To use an environment variable, start your application under a shell using PYRAMID_RELOAD_TEMPLATES operating system environment variable set to 1, For example:
$ PYRAMID_RELOAD_TEMPLATES=1 bin/pserve myproject.ini
the
To use a setting in the application .ini le for the same purpose, set the pyramid.reload_templates key to true within the applications conguration section, e.g.:
1 2 3
140
CHAPTER
TWELVE
VIEW CONFIGURATION
View lookup is the Pyramid subsystem responsible for nding and invoking a view callable. View conguration controls how view lookup operates in your application. During any given request, view conguration information is compared against request data by the view lookup subsystem in order to nd the best view callable for that request. In earlier chapters, you have been exposed to a few simple view conguration declarations without much explanation. In this chapter we will explore the subject in detail.
Non-Predicate Arguments permission The name of a permission that the user must possess in order to invoke the view callable. See Conguring View Security for more information about view security and permissions. If permission is not supplied, no permission is registered for this view (its accessible by any caller).
142
12.1. MAPPING A RESOURCE OR URL PATTERN TO A VIEW CALLABLE attr The view machinery defaults to using the __call__ method of the view callable (or the function itself, if the view callable is a function) to obtain a response. The attr value allows you to vary the method attribute used to obtain the response. For example, if your view was a class, and the class has a method named index and you wanted to use this method instead of the class __call__ method to return the response, youd say attr="index" in the view conguration for the view. This is most useful when the view denition is a class. If attr is not supplied, None is used (implying the function itself if the view is a function, or the __call__ callable attribute if the view is a class). renderer Denotes the renderer implementation which will be used to construct a response from the associated view callables return value. (see also Renderers). This is either a single string term (e.g. json) or a string implying a path or asset specication (e.g. templates/views.pt) naming a renderer implementation. If the renderer value does not contain a dot (.), the specied string will be used to look up a renderer implementation, and that renderer implementation will be used to construct a response from the view return value. If the renderer value contains a dot (.), the specied term will be treated as a path, and the lename extension of the last element in the path will be used to look up the renderer implementation, which will be passed the full path. When the renderer is a path, although a path is usually just a simple relative pathname (e.g. templates/foo.pt, implying that a template named foo.pt is in the templates directory relative to the directory of the current package), a path can be absolute, starting with a slash on UNIX or a drive letter prex on Windows. The path can alternately be a asset specication in the form some.dotted.package_name:relative/path, making it possible to address template assets which live in a separate package. The renderer attribute is optional. If it is not dened, the null renderer is assumed (no rendering is performed and the value is passed back to the upstream Pyramid machinery unchanged). Note that if the view callable itself returns a response (see View Callable Responses), the specied renderer implementation is never called. http_cache When you supply an http_cache value to a view conguration, the Expires and Cache-Control headers of a response generated by the associated view callable are modied. The value for http_cache may be one of the following: A nonzero integer. If its a nonzero integer, its treated as a number of seconds. This number of seconds will be used to compute the Expires header and the Cache-Control: max-age parameter of responses to requests which call this view. For example: http_cache=3600 instructs the requesting browser to cache this response for an hour, please.
143
12. VIEW CONFIGURATION A datetime.timedelta instance. If its a datetime.timedelta instance, it will be converted into a number of seconds, and that number of seconds will be used to compute the Expires header and the Cache-Control: max-age parameter of responses to requests which call this view. For example: http_cache=datetime.timedelta(days=1) instructs the requesting browser to cache this response for a day, please. Zero (0). If the value is zero, the Cache-Control and Expires headers present in all responses from this view will be composed such that client browser cache (and any intermediate caches) are instructed to never cache the response. A two-tuple. If its a two tuple (e.g. http_cache=(1, {public:True})), the rst value in the tuple may be a nonzero integer or a datetime.timedelta instance; in either case this value will be used as the number of seconds to cache the response. The second value in the tuple must be a dictionary. The values present in the dictionary will be used as input to the Cache-Control response header. For example: http_cache=(3600, {public:True}) means cache for an hour, and add public to the Cache-Control header of the response. All keys and values supported by the webob.cachecontrol.CacheControl interface may be added to the dictionary. Supplying {public:True} is equivalent to calling response.cache_control.public = True. Providing a non-tuple value as http_cache is response.cache_expires(value) within your views body. equivalent to calling
Providing a two-tuple value as http_cache is equivalent to calling response.cache_expires(value[0], **value[1]) within your views body. If you wish to avoid inuencing, the Expires header, and instead wish to only inuence Cache-Control headers, pass a tuple as http_cache with the rst element of None, e.g.: (None, {public:True}). wrapper The view name of a different view conguration which will receive the response body of this view as the request.wrapped_body attribute of its own request, and the response returned by this view as the request.wrapped_response attribute of its own request. Using a wrapper makes it possible to chain views together to form a composite response. The response of the outermost wrapper view will be returned to the user. The wrapper view will be found as any view is found: see View Conguration. The best wrapper view will be found based on the lookup ordering: under the hood this wrapper view is looked up via pyramid.view.render_view_to_response(context, request, wrapper_viewname). The context and request of a wrapper view is the same context and request of the inner view. If wrapper is not supplied, no wrapper view is used. 144
12.1. MAPPING A RESOURCE OR URL PATTERN TO A VIEW CALLABLE decorator A dotted Python name to a function (or the function itself) which will be used to decorate the registered view callable. The decorator function will be called with the view callable as a single argument. The view callable it is passed will accept (context, request). The decorator must return a replacement view callable which also accepts (context, request). mapper A Python object or dotted Python name which refers to a view mapper, or None. By default it is None, which indicates that the view should use the default view mapper. This plug-point is useful for Pyramid extension developers, but its not very useful for civilians who are just developing stock Pyramid applications. Pay no attention to the man behind the curtain.
Predicate Arguments These arguments modify view lookup behavior. In general, the more predicate arguments that are supplied, the more specic, and narrower the usage of the congured view. name The view name required to match this view callable. A name argument is typically only used when your application uses traversal. Read Traversal to understand the concept of a view name. If name is not supplied, the empty string is used (implying the default view). context An object representing a Python class that the context resource must be an instance of or the interface that the context resource must provide in order for this view to be found and called. This predicate is true when the context resource is an instance of the represented class or if the context resource provides the represented interface; it is otherwise false. If context is not supplied, the value None, which matches any resource, is used. route_name If route_name is supplied, the view callable will be invoked only when the named route has matched. This value must match the name of a route conguration declaration (see URL Dispatch) that must match before this view will be called. Note that the route conguration referred to by route_name will usually have a *traverse token in the value of its pattern, representing a part of the path that will be used by traversal against the result of the routes root factory. If route_name is not supplied, the view callable will only have a chance of being invoked if no other route was matched. This is when the request/context pair found via resource location does not indicate it matched any congured route. request_type This value should be an interface that the request must provide in order for this view to be found and called. If request_type is not supplied, the value None is used, implying any request type. This is an advanced feature, not often used by civilians. 145
12. VIEW CONFIGURATION request_method This value can be a string (typically "GET", "POST", "PUT", "DELETE", or "HEAD") representing an HTTP REQUEST_METHOD. A view declaration with this argument ensures that the view will only be called when the requests method attribute (aka the REQUEST_METHOD of the WSGI environment) string matches the supplied value. If request_method is not supplied, the view will be invoked regardless of the REQUEST_METHOD of the WSGI environment. request_param This value can be any string or a sequence of strings. A view declaration with this argument ensures that the view will only be called when the request has a key in the request.params dictionary (an HTTP GET or POST variable) that has a name which matches the a supplied value. If any value supplied has a = sign in it, e.g. request_param="foo=123", then the key (foo) must both exist in the request.params dictionary, and the value must match the right hand side of the expression (123) for the view to match the current request. If request_param is not supplied, the view will be invoked without consideration of keys and values in the request.params dictionary. match_param
This param may be either a single string of the format key=value or a dict of key/value pairs. This argument ensures that the view will only be called when the request has key/value pairs in its matchdict that equal those supplied in the predicate. e.g. match_param="action=edit" would require the action parameter in the matchdict match the right hande side of the expression (edit) for the view to match the current request. If the match_param is a dict, every key/value pair must match for the predicate to pass. If match_param is not supplied, the view will be invoked without consideration of the keys and values in request.matchdict. containment This value should be a reference to a Python class or interface that a parent object in the context resources lineage must provide in order for this view to be found and called. The resources in your resource tree must be location-aware to use this feature. If containment is not supplied, the interfaces and classes in the lineage are not considered when deciding whether or not to invoke the view callable. See Location-Aware Resources for more information about location-awareness. 146
12.1. MAPPING A RESOURCE OR URL PATTERN TO A VIEW CALLABLE xhr This value should be either True or False. If this value is specied and is True, the WSGI environment must possess an HTTP_X_REQUESTED_WITH (aka X-Requested-With) header that has the value XMLHttpRequest for the associated view callable to be found and called. This is useful for detecting AJAX requests issued from jQuery, Prototype and other Javascript libraries. If xhr is not specied, the HTTP_X_REQUESTED_WITH HTTP header is not taken into consideration when deciding whether or not to invoke the associated view callable. accept The value of this argument represents a match query for one or more mimetypes in the Accept HTTP request header. If this value is specied, it must be in one of the following forms: a mimetype match token in the form text/plain, a wildcard mimetype match token in the form text/* or a match-all wildcard mimetype match token in the form */*. If any of the forms matches the Accept header of the request, this predicate will be true. If accept is not specied, the HTTP_ACCEPT HTTP header is not taken into consideration when deciding whether or not to invoke the associated view callable. header This value represents an HTTP header name or a header name/value pair. If header is specied, it must be a header name or a headername:headervalue pair. If header is specied without a value (a bare header name only, e.g. If-Modified-Since), the view will only be invoked if the HTTP header exists with any value in the request. If header is specied, and possesses a name/value pair (e.g. User-Agent:Mozilla/.*), the view will only be invoked if the HTTP header exists and the HTTP header matches the value requested. When the headervalue contains a : (colon), it will be considered a name/value pair (e.g. User-Agent:Mozilla/.* or Host:localhost). The value portion should be a regular expression. Whether or not the value represents a header name or a header name/value pair, the case of the header name is not signicant. If header is not specied, the composition, presence or absence of HTTP headers is not taken into consideration when deciding whether or not to invoke the associated view callable. path_info This value represents a regular expression pattern that will be tested against the PATH_INFO WSGI environment variable to decide whether or not to call the associated view callable. If the regex matches, this predicate will be True. If path_info is not specied, the WSGI PATH_INFO is not taken into consideration when deciding whether or not to invoke the associated view callable.
147
12. VIEW CONFIGURATION check_csrf If specied, this value should be one of None, True, False, or a string representing the check name. If the value is True or a string, CSRF checking will be performed. If the value is False or None, CSRF checking will not be performed. If the value provided is a string, that string will be used as the check name. If the value provided is True, csrf_token will be used as the check name. If CSRF checking is performed, the checked value will be the value of request.params[check_name]. This value will be compared against the value of request.session.get_csrf_token(), and the check will pass if these two values are the same. If the check passes, the associated view will be permitted to execute. If the check fails, the associated view will not be permitted to execute. Note that using this feature requires a session factory to have been congured. New in version 1.4a2. physical_path If specied, this value should be a string or a tuple representing the physical path of the context found via traversal for this predicate to match as true. For example: physical_path=/ or physical_path=/a/b/c or physical_path=(, a, b, c). This is not a path prex match or a regex, its a whole-path match. Its useful when you want to always potentially show a view when some object is traversed to, but you cant be sure about what kind of object it will be, so you cant use the context predicate. The individual path elements inbetween slash characters or in tuple elements should be the Unicode representation of the name of the resource and should not be encoded in any way. New in version 1.4a3. effective_principals If specied, this value should be a principal identier or a sequence of principal identiers. If the pyramid.security.effective_principals() method indicates that every principal named in the argument list is present in the current request, this predicate will return True; otherwise it will return False. For example: effective_principals=pyramid.security.Authenticated or effective_principals=(fred, group:admins). New in version 1.4a4. custom_predicates If custom_predicates is specied, it must be a sequence of references to custom predicate callables. Use custom predicates when no set of predened predicates do what you need. Custom predicates can be combined with predened predicates as necessary. Each custom predicate callable should accept two arguments: context and request and should return either True or False after doing arbitrary evaluation of the context resource and/or the request. If all callables return True, the associated view callable will be considered viable for a given request. If custom_predicates is not specied, no custom predicates are used. predicates Pass a key/value pair here to use a third-party predicate registered via pyramid.config.Configurator.add_view_predicate(). More than one key/value pair can be used at the same time. See View and Route Predicates for more information about third-party predicates. New in version 1.4a1. 148
Using this feature tends to slows down application startup slightly, as more work is performed at application startup to scan for view conguration declarations. For maximum startup performance, use the view conguration method described in Adding View Conguration Using add_view() instead. The view_config decorator can be used to associate view conguration information with a function, method, or class that acts as a Pyramid view callable. Heres an example of the view_config decorator that lives within a Pyramid application module views.py:
1 2 3 4 5 6 7
from resources import MyResource from pyramid.view import view_config from pyramid.response import Response @view_config(route_name=ok, request_method=POST, permission=read) def my_view(request): return Response(OK)
Using this decorator as above replaces the need to add this imperative conguration stanza:
1 2
from pyramid.response import Response from pyramid.view import view_config @view_config() def my_view(request): """ My view """ return Response()
Such a registration as the one directly above implies that the view name will be my_view, registered with a context argument that matches any resource type, using no permission, registered against requests with any request method, request type, request param, route name, or containment. The mere existence of a @view_config decorator doesnt sufce to perform view conguration. All that the decorator does is annotate the function with your conguration declarations, it doesnt process them. To make Pyramid process your pyramid.view.view_config declarations, you must use the scan method of a pyramid.config.Configurator: 149
1 2 3
Please see Declarative Conguration for detailed information about what happens when code is scanned for conguration declarations resulting from use of decorators like view_config. See pyramid.cong for additional API arguments to the scan() method. For example, the method allows you to supply a package argument to better control exactly which code will be scanned. All arguments to the view_config decorator mean precisely the same thing as they would if they were passed as arguments to the pyramid.config.Configurator.add_view() method save for the view argument. Usage of the view_config decorator is a form of declarative conguration, while pyramid.config.Configurator.add_view() is a form of imperative conguration. However, they both do the same thing. @view_config Placement A view_config decorator can be placed in various points in your application. If your view callable is a function, it may be used as a function decorator:
1 2 3 4 5 6
from pyramid.view import view_config from pyramid.response import Response @view_config(route_name=edit) def edit(request): return Response(edited!)
If your view callable is a class, the decorator can also be used as a class decorator in Python 2.6 and better (Python 2.5 and below do not support class decorators). All the arguments to the decorator are the same when applied against a class as when they are applied against a function. For example:
1 2 3 4 5 6 7 8 9 10
from pyramid.response import Response from pyramid.view import view_config @view_config(route_name=hello) class MyView(object): def __init__(self, request): self.request = request def __call__(self): return Response(hello)
150
12.1. MAPPING A RESOURCE OR URL PATTERN TO A VIEW CALLABLE You can use the view_config decorator as a simple callable to manually decorate classes in Python 2.5 and below without the decorator syntactic sugar, if you wish:
1 2 3 4 5 6 7 8 9 10 11
from pyramid.response import Response from pyramid.view import view_config class MyView(object): def __init__(self, request): self.request = request def __call__(self): return Response(hello) my_view = view_config(route_name=hello)(MyView)
More than one view_config decorator can be stacked on top of any number of others. Each decorator creates a separate view registration. For example:
1 2 3 4 5 6 7
from pyramid.view import view_config from pyramid.response import Response @view_config(route_name=edit) @view_config(route_name=change) def edit(request): return Response(edited!)
This registers the same view under two different names. The decorator can also be used against a method of a class:
1 2 3 4 5 6 7 8 9 10
from pyramid.response import Response from pyramid.view import view_config class MyView(object): def __init__(self, request): self.request = request @view_config(route_name=hello) def amethod(self): return Response(hello)
When the decorator is used against a method of a class, a view is registered for the class, so the class constructor must accept an argument list in one of two forms: either it must accept a single argument request or it must accept two arguments, context, request. 151
12. VIEW CONFIGURATION The method which is decorated must return a response. Using the decorator against a particular method of a class is equivalent to using the attr parameter in a decorator attached to the class itself. For example, the above registration implied by the decorator being used against the amethod method could be spelled equivalently as the below:
1 2 3 4 5 6 7 8 9 10
from pyramid.response import Response from pyramid.view import view_config @view_config(attr=amethod, route_name=hello) class MyView(object): def __init__(self, request): self.request = request def amethod(self): return Response(hello)
from pyramid.response import Response def hello_world(request): return Response(hello!) # config is assumed to be an instance of the # pyramid.config.Configurator class config.add_view(hello_world, route_name=hello)
The rst argument, view, is required. It must either be a Python object which is the view itself or a dotted Python name to such an object. In the above example, view is the hello_world function. All other arguments are optional. See pyramid.config.Configurator.add_view() for more information. When you use only add_view() to add view congurations, you dont need to issue a scan in order for the view conguration to take effect.
152
If you use a class as a view, you can use the pyramid.view.view_defaults class decorator on the class to provide defaults to the view conguration information used by every @view_config decorator that decorates a method of that class. For instance, if youve got a class that has methods that represent REST actions, all which are mapped to the same route, but different request methods, instead of this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
from pyramid.view import view_config from pyramid.response import Response class RESTView(object): def __init__(self, request): self.request = request @view_config(route_name=rest, request_method=GET) def get(self): return Response(get) @view_config(route_name=rest, request_method=POST) def post(self): return Response(post) @view_config(route_name=rest, request_method=DELETE) def delete(self): return Response(delete)
from pyramid.view import view_defaults from pyramid.view import view_config from pyramid.response import Response @view_defaults(route_name=rest) class RESTView(object): def __init__(self, request): self.request = request
153
10 11 12 13 14 15 16 17 18 19 20
@view_config(request_method=GET) def get(self): return Response(get) @view_config(request_method=POST) def post(self): return Response(post) @view_config(request_method=DELETE) def delete(self): return Response(delete)
In the above example, we were able to take the route_name=rest argument out of the call to each individual @view_config statement, because we used a @view_defaults class decorator to provide the argument as a default to each view method it possessed. Arguments passed to @view_config will override any default passed to @view_defaults. The view_defaults class decorator can also provide defaults to the pyramid.config.Configurator.add_view() directive when a decorated class is passed to that directive as its view argument. For example, instead of this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
from pyramid.response import Response from pyramid.config import Configurator class RESTView(object): def __init__(self, request): self.request = request def get(self): return Response(get) def post(self): return Response(post) def delete(self): return Response(delete) if __name__ == __main__: config = Configurator() config.add_route(rest, /rest) config.add_view( RESTView, route_name=rest, attr=get, request_method=GET) config.add_view( RESTView, route_name=rest, attr=post, request_method=POST)
154
24 25
To reduce the amount of repetion in the config.add_view statements, we can move the route_name=rest argument to a @view_default class decorator on the RESTView class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
from pyramid.view import view_config from pyramid.response import Response from pyramid.config import Configurator @view_defaults(route_name=rest) class RESTView(object): def __init__(self, request): self.request = request def get(self): return Response(get) def post(self): return Response(post) def delete(self): return Response(delete) if __name__ == __main__: config = Configurator() config.add_route(rest, /rest) config.add_view(RESTView, attr=get, request_method=GET) config.add_view(RESTView, attr=post, request_method=POST) config.add_view(RESTView, attr=delete, request_method=DELETE)
pyramid.view.view_defaults accepts the same set of arguments that pyramid.view.view_config does, and they have the same meaning. Each argument passed to view_defaults provides a default for the view congurations of methods of the class its decorating. Normal Python inheritance rules apply to defaults added via view_defaults. For example:
1 2 3 4 5 6
155
12. VIEW CONFIGURATION The Bar class above will inherit its view defaults from the arguments passed to the view_defaults decorator of the Foo class. To prevent this from happening, use a view_defaults decorator without any arguments on the subclass:
1 2 3 4 5 6 7
The view_defaults decorator only works as a class decorator; using it against a function or a method will produce nonsensical results.
When an authorization policy is enabled, this view will be protected with the add permission. The view will not be called if the user does not possess the add permission relative to the current context. Instead the forbidden view result will be returned to the client as per Protecting Views with Permissions.
When a non-None http_cache argument is passed to a view conguration, Pyramid will set Expires and Cache-Control response headers in the resulting response, causing browsers to cache the response data for some time. See http_cache in Non-Predicate Arguments for the allowable values and what they mean. Sometimes its undesirable to have these headers set as the result of returning a response from a view, even though youd like to decorate the view with a view conguration decorator that has http_cache. Perhaps theres an alternate branch in your view code that returns a response that should never be cacheable, while the normal branch returns something that should always be cacheable. If this is the case, set the prevent_auto attribute of the response.cache_control object to a non-False value. For example, the below view callable is congured with a @view_config decorator that indicates any response from the view should be cached for 3600 seconds. However, the view itself prevents caching from taking place unless theres a should_cache GET or POST variable:
from pyramid.view import view_config @view_config(http_cache=3600) def view(request): response = Response() if not should_cache in request.params: response.cache_control.prevent_auto = True return response
Note that the http_cache machinery will overwrite or add to caching headers you set within the view itself unless you use prevent_auto. You can also turn off the effect of http_cache entirely for the duration of a Pyramid application lifetime. To do so, set the PYRAMID_PREVENT_HTTP_CACHE environment variable or the pyramid.prevent_http_cache conguration value setting to a true value. For more information, see Preventing HTTP Caching. Note that setting pyramid.prevent_http_cache will have no effect on caching headers that your application code itself sets. It will only prevent caching headers that would have been set by the Pyramid HTTP caching machinery invoked as the result of the http_cache argument to view conguration.
157
158
CHAPTER
THIRTEEN
STATIC ASSETS
An asset is any le contained within a Python package which is not a Python source code le. For example, each of the following is an asset: a GIF image le contained within a Python package or contained within any subdirectory of a Python package. a CSS le contained within a Python package or contained within any subdirectory of a Python package. a JavaScript source le contained within a Python package or contained within any subdirectory of a Python package. A directory within a package that does not have an __init__.py in it (if it possessed an __init__.py it would be a package). a Chameleon or Mako template le contained within a Python package. The use of assets is quite common in most web development projects. For example, when you create a Pyramid application using one of the available scaffolds, as described in Creating the Project, the directory representing the application contains a Python package. Within that Python package, there are directories full of les which are static assets. For example, theres a static directory which contains .css, .js, and .gif les. These asset les are delivered when a user visits an application URL.
1 2
Under the hood, when this API is called, Pyramid attempts to make sense out of the string myapp:templates/some_template.pt provided by the developer. This string is an asset specication. It is composed of two parts: The package name (myapp) The asset name (templates/some_template.pt), relative to the package directory. The two parts are separated by the colon character. Pyramid uses the Python pkg_resources API to resolve the package name and asset name to an absolute (operating-system-specic) le name. It eventually passes this resolved absolute lesystem path to the Chameleon templating engine, which then uses it to load, parse, and execute the template le. There is a second form of asset specication: a relative asset specication. Instead of using an absolute asset specication which includes the package name, in certain circumstances you can omit the package name from the specication. For example, you might be able to use templates/mytemplate.pt instead of myapp:templates/some_template.pt. Such asset specications are usually relative to a current package. The current package is usually the package which contains the code that uses the asset specication. Pyramid APIs which accept relative asset specications typically describe what the asset is relative to in their individual documentation.
add_static_view() cannot serve a single le, nor can it serve a directory of static les directly relative to the root URL of a Pyramid application. For these features, see Advanced: Serving Static Assets Using a View Callable. Heres an example of a use of add_static_view() that will serve les up from the /var/www/static directory of the computer which runs the Pyramid application as URLs beneath the /static URL prex. 160
1 2
The name represents a URL prex. In order for les that live in the path directory to be served, a URL that requests one of them must begin with that prex. In the example above, name is static, and path is /var/www/static. In English, this means that you wish to serve the les that live in /var/www/static as sub-URLs of the /static URL prex. Therefore, the le /var/www/static/foo.css will be returned when the user visits your applications URL /static/foo.css. A static directory named at path may contain subdirectories recursively, and any subdirectories may hold les; these will be resolved by the static view as you would expect. The Content-Type header returned by the static view for each particular type of le is dependent upon its le extension. By default, all les made available via add_static_view() are accessible by completely anonymous users. Simple authorization can be required, however. To protect a set of static les using a permission, in addition to passing the required name and path arguments, also pass the permission keyword argument to add_static_view(). The value of the permission argument represents the permission that the user must have relative to the current context when the static view is invoked. A user will be required to possess this permission to view any of the les represented by path of the static view. If your static assets must be protected by a more complex authorization scheme, see Advanced: Serving Static Assets Using a View Callable. Heres another example that uses an asset specication instead of an absolute path as the path argument. To convince add_static_view() to serve les up under the /static URL from the a/b/c/static directory of the Python package named some_package, we can use a fully qualied asset specication as the path:
1 2
The path provided to add_static_view() may be a fully qualied asset specication or an absolute path. Instead of representing a URL prex, the name argument of a call to add_static_view() can alternately be a URL. Each of examples weve seen so far have shown usage of the name argument as a URL prex. However, when name is a URL, static assets can be served from an external webserver. In this mode, the name is used as the URL prex when generating a URL using pyramid.request.Request.static_url(). For example, add_static_view() http://example.com/images: may be fed a name argument which is
161
1 2 3
Because add_static_view() is provided with a name argument that is the URL http://example.com/images, subsequent calls to static_url() with paths that start with the path argument passed to add_static_view() will generate a URL something like http://example.com/images/logo.png. The external webserver listening on example.com must be itself congured to respond properly to such a request. The static_url() API is discussed in more detail later in this chapter.
These declarations create URL-accessible directories which have URLs that begin with /static1 and /static2, respectively. The assets in the assets/1 directory of the mypackage package are consulted when a user visits a URL which begins with /static1, and the assets in the assets/2 directory of the mypackage package are consulted when a user visits a URL which begins with /static2. You neednt generate the URLs to static assets by hand in such a conguration. Instead, use the static_url() API to generate them for you. For example:
1 2 3 4 5 6 7 8
from pyramid.chameleon_zpt import render_template_to_response def my_view(request): css_url = request.static_url(mypackage:assets/1/foo.css) js_url = request.static_url(mypackage:assets/2/foo.js) return render_template_to_response(templates/my_template.pt, css_url = css_url, js_url = js_url)
162
13.3. ADVANCED: SERVING STATIC ASSETS USING A VIEW CALLABLE If the request application URL of the running system is http://example.com, the css_url generated above would be: http://example.com/static1/foo.css. The js_url generated above would be http://example.com/static2/foo.js. One benet of using the static_url() function rather than constructing static URLs by hand is that if you need to change the name of a static URL declaration, the generated URLs will continue to resolve properly after the rename. URLs may also be generated by static_url() to static assets that live outside the Pyramid application. This will happen when the add_static_view() API associated with the path fed to static_url() is a URL instead of a view name. For example, the name argument may be http://example.com while the path given may be mypackage:images:
1 2
config.add_static_view(name=http://example.com/images, path=mypackage:images)
Under such a conguration, the URL generated by static_url for assets which begin with mypackage:images will be prexed with http://example.com/images:
1 2
Using static_url() in conjunction with a add_static_view() makes it possible to put static media on a separate webserver during production (if the name argument to add_static_view() is a URL), while keeping static media package-internal and served by the development webserver during development (if the name argument to add_static_view() is a URL prex). To create such a circumstance, we suggest using the pyramid.registry.Registry.settings API in conjunction with a setting in the application .ini le named media_location. Then set the value of media_location to either a prex or a URL depending on whether the application is being run in development or in production (use a different .ini le for production than you do for development). This is just a suggestion for a pattern; any setting name other than media_location could be used.
The following example will not work for applications that use traversal, it will only work if you use URL dispatch exclusively. The root-relative route well be registering will always be matched before traversal takes place, subverting any views registered via add_view (at least those without a route_name). A static_view static view cannot be made root-relative when you use traversal unless its registered as a Not Found view.
To serve les within a directory located on your lesystem at /path/to/static/dir as the result of a catchall route hanging from the root that exists at the end of your routing table, create an instance of the static_view class inside a static.py le in your application root as below.
1 2
For better cross-system exibility, use an asset specication as the argument to static_view instead of a physical absolute lesystem path, e.g. mypackage:static instead of /path/to/mypackage/static.
Subsequently, you may wire the les that are served by this view up to be accessible as /<filename> using a conguration method in your applications startup code.
1 2 3 4 5
# .. every other add_route declaration should come # before this one, as it will, by default, catch all requests config.add_route(catchall_static, /*subpath) config.add_view(myapp.static.static_view, route_name=catchall_static)
The special name *subpath above is used by the static_view view callable to signify the path of the le relative to the directory youre serving.
164
import os from pyramid.response import FileResponse def favicon_view(request): here = os.path.dirname(__file__) icon = os.path.join(here, static, favicon.ico) return FileResponse(icon, request=request)
The above bit of code within favicon_view computes here, which is a path relative to the Python le in which the function is dened. It then creates a pyramid.response.FileResponse using the le path as the responses path argument and the request as the responses request argument. pyramid.response.FileResponse will serve the le as quickly as possible when its used this way. It makes sure to set the right content length and content_type too based on the le extension of the le you pass. You might register such a view via conguration as a view callable that should be called as the result of a traversal:
1
config.add_view(myapp.views.favicon_view, name=favicon.ico)
Because this is a simple view callable, it can be protected with a permission or can be congured to respond under different circumstances using view predicate arguments.
13. STATIC ASSETS its often more convenient to just override the assets that are inappropriate and reuse the application as is. This is particularly true when you reuse some core application over and over again for some set of customers (such as a CMS application, or some bug tracking application), and you want to make arbitrary visual modications to a particular application deployment without forking the underlying code. To this end, Pyramid contains a feature that makes it possible to override one asset with one or more other assets. In support of this feature, a Congurator API exists named pyramid.config.Configurator.override_asset(). This API allows you to override the following kinds of assets dened in any Python package: Individual Chameleon templates. A directory containing multiple Chameleon templates. Individual static les served up by an instance of the pyramid.static.static_view helper class. A directory of static les served up by an instance of the pyramid.static.static_view helper class. Any other asset (or set of assets) addressed by code that uses the setuptools pkg_resources API.
The string value passed to both to_override and override_with sent to the override_asset API is called an asset specication. The colon separator in a specication separates the package name from the asset name. The colon and the following asset name are optional. If they are not specied, the override attempts to resolve every lookup into a package from the directory of another package. For example:
1 2
config.override_asset(to_override=some.package, override_with=another.package)
166
1 2
config.override_asset(to_override=some.package:templates/, override_with=another.package:othertemplates/)
If you wish to override a directory with another directory, you must make sure to attach the slash to the end of both the to_override specication and the override_with specication. If you fail to attach a slash to the end of a specication that points to a directory, you will get unexpected results. You cannot override a directory specication with a le specication, and vice versa: a startup error will occur if you try. You cannot override an asset with itself: a startup error will occur if you try. Only individual package assets may be overridden. Overrides will not traverse through subpackages within an overridden package. This means that if you want to override assets for both some.package:templates, and some.package.views:templates, you will need to register two overrides. The package name in a specication may start with a dot, meaning that the package is relative to the package in which the conguration construction le resides (or the package argument to the Configurator class construction). For example:
1 2
config.override_asset(to_override=.subpackage:templates/, override_with=another.package:templates/)
Multiple calls to override_asset which name a shared to_override but a different override_with specication can be stacked to form a search path. The rst asset that exists in the search path will be used; if no asset exists in the override path, the original asset is used. Asset overrides can actually override assets other than templates and static les. Any software which uses the pkg_resources.get_resource_filename(), pkg_resources.get_resource_stream() or pkg_resources.get_resource_string() APIs will obtain an overridden le when an override is used.
167
168
CHAPTER
FOURTEEN
This chapter is adapted from a portion of the WebOb documentation, originally written by Ian Bicking.
Pyramid uses the WebOb package as a basis for its request and response object implementations. The request object that is passed to a Pyramid view is an instance of the pyramid.request.Request class, which is a subclass of webob.Request. The response returned from a Pyramid view renderer is an instance of the pyramid.response.Response class, which is a subclass of the webob.Response class. Users can also return an instance of pyramid.response.Response directly from a view as necessary. WebOb is a project separate from Pyramid with a separate set of authors and a fully separate set of documentation. Pyramid adds some functionality to the standard WebOb request, which is documented in the pyramid.request API documentation. WebOb provides objects for HTTP requests and responses. Specically it does this by wrapping the WSGI request environment and response status, header list, and app_iter (body) values. WebOb request and response objects provide many conveniences for parsing WSGI requests and forming WSGI responses. WebOb is a nice way to represent raw WSGI requests and responses; however, we wont cover that use case in this document, as users of Pyramid dont typically need to use the WSGIrelated features of WebOb directly. The reference documentation shows many examples of creating requests and using response objects in this manner, however. 169
14.1 Request
The request object is a wrapper around the WSGI environ dictionary. This dictionary contains keys for each header, keys that describe the request (including the path and query string), a le-like object for the request body, and a variety of custom keys. You can always access the environ with req.environ. Some of the most important/interesting attributes of a request object: req.method: The request method, e.g., GET, POST req.GET: A multidict with all the variables in the query string. req.POST: A multidict with all the variables in the request body. This only has variables if the request was a POST and it is a form submission. req.params: A multidict with a combination of everything in req.GET and req.POST. req.body: The contents of the body of the request. This contains the entire request body as a string. This is useful when the request is a POST that is not a form submission, or a request like a PUT. You can also get req.body_file for a le-like object. req.json_body The JSON-decoded contents of the body of the request. See Dealing With A JSONEncoded Request Body. req.cookies: A simple dictionary of all the cookies. req.headers: A dictionary of all the headers. This dictionary is case-insensitive. req.urlvars and req.urlargs: req.urlvars are the keyword parameters associated with the request URL. req.urlargs are the positional parameters. These are set by products like Routes and Selector. Also, for standard HTTP request headers there are usually attributes, for instance: req.accept_language, req.content_length, req.user_agent, as an example. These properties expose the parsed form of each header, for whatever parsing makes sense. For instance, req.if_modified_since returns a datetime object (or None if the header is was not provided).
Full API documentation for the Pyramid request object is available in pyramid.request.
170
14.1. REQUEST
14.1.2 URLs
In addition to these attributes, there are several ways to get the URL of the request. Ill show various values for an example URL http://localhost/app/blog?id=10, where the application is mounted at http://localhost/app. req.url: The full request URL, with query string, e.g., http://localhost/app/blog?id=10 req.host: The host information in the URL, e.g., localhost req.host_url: The URL with the host, e.g., http://localhost req.application_url: The URL of the application (just the SCRIPT_NAME portion of the path, not PATH_INFO). E.g., http://localhost/app req.path_url: The URL of the http://localhost/app/blog application including the PATH_INFO. e.g.,
req.path: The URL including PATH_INFO without the host or scheme. e.g., /app/blog req.path_qs: The URL including PATH_INFO and the query string. e.g, /app/blog?id=10 req.query_string: The query string in the URL, e.g., id=10 req.relative_url(url, to_application=False): Gives a URL, relative to the current URL. If to_application is True, then resolves it relative to req.application_url.
14.1.3 Methods
There are methods of request objects documented in pyramid.request.Request but youll nd that you wont use very many of them. Here are a couple that might be useful: Request.blank(base_url): Creates a new request with blank information, based at the given URL. This can be useful for subrequests and articial requests. You can also use req.copy() to copy an existing request, or for subrequests req.copy_get() which copies the request but always turns it into a GET (which is safer to share for subrequests). req.get_response(wsgi_application): This method calls the given WSGI application with this request, and returns a pyramid.response.Response object. You can also use this for subrequests, or testing. 171
14.1.4 Unicode
Many of the properties in the request object will return unicode values if the request encoding/charset is provided. The client can indicate the charset with something like Content-Type: application/x-www-form-urlencoded; charset=utf8, but browsers seldom set this. You can set the charset with req.charset = utf8, or during instantiation with Request(environ, charset=utf8). If you subclass Request you can also set charset as a class-level attribute. If it is set, then req.POST, req.GET, req.params, and req.cookies will contain unicode strings. Each has a corresponding req.str_* (e.g., req.str_POST) that is always a str, and never unicode.
14.1.5 Multidict
Several attributes of a WebOb request are multidict; structures (such as request.GET, request.POST, and request.params). A multidict is a dictionary where a key can have multiple values. The quintessential example is a query string like ?pref=red&pref=blue; the pref variable has two values: red and blue. In a multidict, when you do request.GET[pref] youll get back only blue (the last value of pref). Sometimes returning a string, and sometimes returning a list, is the cause of frequent exceptions. If you want all the values back, use request.GET.getall(pref). If you want to be sure there is one and only one value, use request.GET.getone(pref), which will raise an exception if there is zero or more than one value for pref. When you use operations like request.GET.items() youll get back something like [(pref, red), (pref, blue)]. All the key/value pairs will show up. Similarly request.GET.keys() returns [pref, pref]. Multidict is a view on a list of tuples; all the keys are ordered, and all the values are ordered. API documentation for a multidict exists as pyramid.interfaces.IMultiDict.
172
14.1. REQUEST pyramid.request.Request.json_body is a property that returns a JSON -decoded representation of the request body. If the request does not have a body, or the body is not a properly JSON-encoded value, an exception will be raised when this attribute is accessed. This attribute is useful when you invoke a Pyramid view callable via e.g. jQuerys $.ajax function, which has the potential to send a request with a JSON-encoded body. Using request.json_body is equivalent to:
from json import loads loads(request.body, encoding=request.charset)
Heres how to construct an AJAX request in Javascript using jQuery that allows you to use the request.json_body attribute when the request is sent to a Pyramid application:
jQuery.ajax({type:POST, url: http://localhost:6543/, // the pyramid server data: JSON.stringify({a:1}), contentType: application/json; charset=utf-8});
When such a request reaches a view in your application, the request.json_body attribute will be available in the view callable body.
@view_config(renderer=string) def aview(request): print request.json_body return OK
For bonus points, heres a bit of client-side code that will produce a request that has a body suitable for reading via request.json_body using Pythons urllib2 instead of a Javascript AJAX request:
import urllib2 import json json_payload = json.dumps({a:1}) headers = {Content-Type:application/json; charset=utf-8} req = urllib2.Request(http://localhost:6543/, json_payload, headers) resp = urllib2.urlopen(req)
173
from mypackage.models import DBSession from pyramid.events import subscriber from pyramid.events import NewRequest def cleanup_callback(request): DBSession.remove() @subscriber(NewRequest) def add_cleanup_callback(event): event.request.add_finished_callback(cleanup_callback)
Registering the cleanup_callback nished callback at the start of a request (by causing the add_cleanup_callback to receive a pyramid.events.NewRequest event at the start of each request) will cause the DBSession to be removed whenever request processing has ended. Note that in the example above, for the pyramid.events.subscriber decorator to work, the pyramid.config.Configurator.scan() method must be called against your mypackage package during application initialization.
This is only an example. In particular, it is not necessary to cause DBSession.remove to be called in an application generated from any Pyramid scaffold, because these all use the pyramid_tm package. The cleanup done by DBSession.remove is unnecessary when pyramid_tm middleware is congured into the application.
14.2. RESPONSE
14.2 Response
The Pyramid response object can be imported as pyramid.response.Response. This class is a subclass of the webob.Response class. The subclass does not add or change any functionality, so the WebOb Response documentation will be completely relevant for this class as well. A response object has three fundamental parts: response.status: The response code plus reason message, like 200 OK. To set the code without a message, use status_int, i.e.: response.status_int = 200. response.headerlist: A list of all the headers, like [(Content-Type, text/html)]. Theres a case-insensitive multidict in response.headers that also allows you to access these same headers. response.app_iter: An iterable (such as a list or generator) that will produce the content of the response. This is also accessible as response.body (a string), response.unicode_body (a unicode object, informed by response.charset), and response.body_file (a le-like object; writing to it appends to app_iter). Everything else in the object typically derives from this underlying state. Here are some highlights: response.content_type The content type not including the charset parameter. Typical use: response.content_type = text/html. response.charset: The charset parameter of the content-type, it also informs encoding in response.unicode_body. response.content_type_params is a dictionary of all the parameters. response.set_cookie(key, value, max_age=None, path=/, ...): Set a cookie. The keyword arguments control the various cookie parameters. The max_age argument is the length for the cookie to live in seconds (you may also use a timedelta object). The Expires key will also be set based on the value of max_age. response.delete_cookie(key, path=/, domain=None): Delete a cookie from the client. This sets max_age to 0 and the cookie value to . response.cache_expires(seconds=0): This makes this response cacheable for the given number of seconds, or if seconds is 0 then the response is uncacheable (this also sets the Expires header). response(environ, start_response): The response object is a WSGI application. As an application, it acts according to how you create it. It can do conditional responses if you pass conditional_response=True when instantiating (or set that attribute later). It can also do HEAD and Range requests. 175
14.2.1 Headers
Like the request, most HTTP response headers are available as properties. These are parsed, so you can do things like response.last_modified = os.path.getmtime(filename). The details are available in the extracted Response documentation.
The status defaults to 200 OK. The content_type does not default to anything, though if you subclass pyramid.response.Response and set default_content_type you can override this behavior.
from pyramid.httpexceptions import HTTPNotFound from pyramid.httpexceptions import HTTPMovedPermanently response = HTTPNotFound(There is no such resource) # or: response = HTTPMovedPermanently(location=new_url)
CHAPTER
FIFTEEN
SESSIONS
A session is a namespace which is valid for some period of continual activity that can be used to represent a users interaction with a web application. This chapter describes how to congure sessions, what session implementations Pyramid provides out of the box, how to store and retrieve data from sessions, and two session-specic features: ash messages, and cross-site request forgery attack prevention.
177
15. SESSIONS
1 2 3 4 5
from pyramid.session import UnencryptedCookieSessionFactoryConfig my_session_factory = UnencryptedCookieSessionFactoryConfig(itsaseekreet) from pyramid.config import Configurator config = Configurator(session_factory = my_session_factory)
Note the very long, very explicit name for UnencryptedCookieSessionFactoryConfig. Its trying to tell you that this implementation is, by default, unencrypted. You should not use it when you keep sensitive information in the session object, as the information can be easily read by both users of your application and third parties who have access to your users network trafc. And if you use this sessioning implementation, and you inadvertently create a cross-site scripting vulnerability in your application, because the session data is stored unencrypted in a cookie, it will also be easier for evildoers to obtain the current users cross-site scripting token. In short, use a different session factory implementation (preferably one which keeps session data on the server) for anything but the most basic of applications where session security doesnt matter, and you are sure your application has no cross-site scripting vulnerabilities.
from pyramid.response import Response def myview(request): session = request.session if abc in session: session[fred] = yes session[abc] = 123 if fred in session: return Response(Fred was in the session) else: return Response(Fred was not in the session)
You can use a session much like a Python dictionary. It supports all dictionary methods, along with some extra attributes, and methods. Extra attributes: 178
15.3. USING ALTERNATE SESSION FACTORIES created An integer timestamp indicating the time that this session was created. new A boolean. If new is True, this session is new. Otherwise, it has been constituted from data that was already serialized. Extra methods: changed() Call this when you mutate a mutable value in the session namespace. See the gotchas below for details on when, and why you should call this. invalidate() Call this when you want to invalidate the session (dump all data, and perhaps set a clearing cookie). The formal denition of the methods and attributes supported by the session object are in the pyramid.interfaces.ISession documentation. Some gotchas: Keys and values of session data must be pickleable. This means, typically, that they are instances of basic types of objects, such as strings, lists, dictionaries, tuples, integers, etc. If you place an object in a session data key or value that is not pickleable, an error will be raised when the session is serialized. If you place a mutable value (for example, a list or a dictionary) in a session object, and you subsequently mutate that value, you must call the changed() method of the session object. In this case, the session has no way to know that is was modied. However, when you modify a session object directly, such as setting a value (i.e., __setitem__), or removing a key (e.g., del or pop), the session will automatically know that it needs to re-serialize its data, thus calling changed() is unnecessary. There is no harm in calling changed() in either case, so when in doubt, call it after youve changed sessioning data.
179
15. SESSIONS
The flash() method appends a message to a ash queue, creating the queue if necessary. flash() accepts three arguments: flash(message, queue=, allow_duplicate=True) The message argument is required. It represents a message you wish to later display to a user. It is usually a string but the message you provide is not modied in any way. The queue argument allows you to choose a queue to which to append the message you provide. This can be used to push different kinds of messages into ash storage for later display in different places on a page. You can pass any name for your queue, but it must be a string. Each queue is independent, and can be popped by pop_flash() or examined via peek_flash() separately. queue defaults to the empty string. The empty string represents the default ash message queue.
180
request.session.flash(msg, myappsqueue)
The allow_duplicate argument defaults to True. If this is False, and you attempt to add a message value which is already present in the queue, it will not be added.
Calling session.pop_flash() again like above without a corresponding session.flash() will return an empty list, because the queue has already been popped.
1 2 3 4 5
call
to
181
15. SESSIONS
1 2 3 4 5 6 7 8 9
>>> request.session.flash(info message) >>> request.session.peek_flash() [info message] >>> request.session.peek_flash() [info message] >>> request.session.pop_flash() [info message] >>> request.session.peek_flash() []
The session.get_csrf_token() method accepts no arguments. It returns a CSRF token string. If session.get_csrf_token() or session.new_csrf_token() was invoked previously for this session, the existing token will be returned. If no CSRF token previously existed for this session, a new token will be will be set into the session and returned. The newly created token will be opaque and randomized. You can use the returned token as the value of a hidden eld in a form that posts to a method that requires elevated privileges. The handler for the form post should use session.get_csrf_token() again to obtain the current CSRF token related to the user from the session, and compare it to the value of the hidden form eld. For example, if your form rendering included the CSRF token obtained via session.get_csrf_token() as a hidden input eld named csrf_token:
182
1 2 3
token = request.session.get_csrf_token() if token != request.POST[csrf_token]: raise ValueError(CSRF token did not match)
183
15. SESSIONS
184
CHAPTER
SIXTEEN
USING EVENTS
An event is an object broadcast by the Pyramid framework at interesting points during the lifetime of an application. You dont need to use events in order to create most Pyramid applications, but they can be useful when you want to perform slightly advanced operations. For example, subscribing to an event can allow you to run some code as the result of every new request. Events in Pyramid are always broadcast by the framework. However, they only become useful when you register a subscriber. A subscriber is a function that accepts a single argument named event:
1 2
The above is a subscriber that simply prints the event to the console when its called. The mere existence of a subscriber function, however, is not sufcient to arrange for it to be called. To arrange for the subscriber to be called, youll need to use the pyramid.config.Configurator.add_subscriber() method or youll need to use the pyramid.events.subscriber() decorator to decorate a function found via a scan.
185
1 2 3 4 5 6 7 8
from 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)
The rst argument to add_subscriber() is the subscriber function (or a dotted Python name which refers to a subscriber callable); the second argument is the event type.
from pyramid.events import NewRequest from pyramid.events import subscriber @subscriber(NewRequest) def mysubscriber(event): event.request.foo = 1
When the subscriber() decorator is used a scan must be performed against the package containing the decorated function for the decorator to have any effect. Either of the above registration examples implies that every time the Pyramid framework emits an event object that supplies an pyramid.events.NewRequest interface, the mysubscriber function will be called with an event object. As you can see, a subscription is made in terms of a class (such as pyramid.events.NewResponse). The event object sent to a subscriber will always be an object that possesses an interface. For pyramid.events.NewResponse, that interface is pyramid.interfaces.INewResponse. The interface documentation provides information about available attributes and methods of the event objects. The return value of a subscriber function is ignored. Subscribers to the same event type are not guaranteed to be called in any particular order relative to each other. All the concrete Pyramid event types are documented in the pyramid.events API documentation. 186
16.3. AN EXAMPLE
16.3 An Example
If you create event listener functions in a subscribers.py le in your application like so:
1 2 3 4 5
def handle_new_request(event): print request, event.request def handle_new_response(event): print response, event.response
You may congure these functions to be called at the appropriate times by adding the following code to your applications conguration startup:
1 2 3 4 5 6
Either mechanism causes the functions in subscribers.py to be registered as event subscribers. Under this conguration, when the application is run, each time a new request or response is detected, a message will be printed to the console. Each of our subscriber functions accepts an event object and prints an attribute of the event object. This begs the question: how can we know which attributes a particular event has? We know that pyramid.events.NewRequest event objects have a request attribute, which is a request object, because the interface dened at pyramid.interfaces.INewRequest says it must. Likewise, we know that pyramid.interfaces.NewResponse events have a response attribute, which is a response object constructed by your application, because the interface dened at pyramid.interfaces.INewResponse says it must (pyramid.events.NewResponse objects also have a request).
187
188
CHAPTER
SEVENTEEN
Where a conguration le setting exists with the same meaning as an environment variable, and both are present at application startup time, the environment variable setting takes precedence. The term conguration le setting name refers to a key in the .ini conguration for your application. The conguration le setting names documented in this chapter are reserved for Pyramid use. You should not use them to indicate application-specic conguration settings.
189
For backwards compatibility purposes, aliases can be used for congurating asset reloading: PYRAMID_RELOAD_RESOURCES (envvar) and pyramid.reload_resources (cong le).
191
Using PasteDeploy Using the following pyramid.includes setting in the PasteDeploy .ini le in your application:
192
from pyramid.config import Configurator def main(global_config, **settings): config = Configurator(settings=settings) # ... config.include(pyramid_debugtoolbar) config.include(pyramid_tm) # ...
Plain Python Using the following pyramid.includes setting in your plain-Python Pyramid application:
1 2 3 4 5
from pyramid.config import Configurator if __name__ == __main__: settings = {pyramid.includes:pyramid_debugtoolbar pyramid_tm} config = Configurator(settings=settings)
from pyramid.config import Configurator if __name__ == __main__: settings = {} config = Configurator(settings=settings) config.include(pyramid_debugtoolbar) config.include(pyramid_tm)
193
from pyramid.config import Configurator def main(global_config, **settings): settings[pyramid.tweens] = [ pyramid_debugtoolbar.toolbar.tween_factory, pyramid.tweebs.excview_tween_factory, pyramid_tm.tm_tween_factory, ] config = Configurator(settings=settings)
195
196
17.13. EXAMPLES
This feature is new in Pyramid 1.1. Cong File Setting Name mako.preprocessor
17.13 Examples
Lets presume your conguration le is named MyProject.ini, and there is a section representing your application named [app:main] within the le that represents your Pyramid application. The conguration le settings documented in the above Cong File Setting Name column would go in the [app:main] section. Heres an example of such a section:
197
1 2 3 4
You can also use environment variables to accomplish the same purpose for settings documented as such. For example, you might start your Pyramid application using the following command line:
$ PYRAMID_DEBUG_AUTHORIZATION=1 PYRAMID_RELOAD_TEMPLATES=1 \ bin/paster serve MyProject.ini
If you started your application this way, your Pyramid application would behave in the same manner as if you had placed the respective settings in the [app:main] section of your applications .ini le. If you want to turn all debug settings (every setting that starts with pyramid.debug_). on in one fell swoop, you can use PYRAMID_DEBUG_ALL=1 as an environment variable setting or you may use pyramid.debug_all=true in the cong le. Note that this does not affect settings that do not start with pyramid.debug_* such as pyramid.reload_templates. If you want to turn all pyramid.reload settings (every setting that starts with pyramid.reload_) on in one fell swoop, you can use PYRAMID_RELOAD_ALL=1 as an environment variable setting or you may use pyramid.reload_all=true in the cong le. Note that this does not affect settings that do not start with pyramid.reload_* such as pyramid.debug_notfound.
Specifying conguration settings via environment variables is generally most useful during development, where you may wish to augment or override the more permanent settings in the conguration le. This is useful because many of the reload and debug settings may have performance or security (i.e., disclosure) implications that make them undesirable in a production environment.
Between
The difference between pyramid.reload_assets and pyramid.reload_templates is a bit subtle. Templates are themselves also treated by Pyramid as asset les (along with other static les), so the distinction can be confusing. Its helpful to read Overriding Assets for some context about assets in general. 198
17.15. ADDING A CUSTOM SETTING When pyramid.reload_templates is true, Pyramid takes advantage of the underlying templating systems ability to check for le modications to an individual template le. When pyramid.reload_templates is true but pyramid.reload_assets is not true, the template lename returned by the pkg_resources package (used under the hood by asset resolution) is cached by Pyramid on the rst request. Subsequent requests for the same template le will return a cached template lename. The underlying templating system checks for modications to this particular le for every request. Setting pyramid.reload_templates to True doesnt affect performance dramatically (although it should still not be used in production because it has some effect). However, when pyramid.reload_assets is true, Pyramid will not cache the template lename, meaning you can see the effect of changing the content of an overridden asset directory for templates without restarting the server after every change. Subsequent requests for the same template le may return different lenames based on the current state of overridden asset directories. Setting pyramid.reload_assets to True affects performance dramatically, slowing things down by an order of magnitude for each template rendering. However, its convenient to enable when moving les around in overridden asset directories. pyramid.reload_assets makes the system very slow when templates are in use. Never set pyramid.reload_assets to True on a production system.
In the main() function that represents the place that your Pyramid WSGI application is created, anticipate that youll be getting this key/value pair as a setting and do any type conversion necessary. If youve done any type conversion of your custom value, reset the converted values into the settings dictionary before you pass the dictionary as settings to the Congurator. For example:
def main(global_config, **settings): # ... from pyramid.settings import asbool debug_frobnosticator = asbool(settings.get( debug_frobnosticator, false)) settings[debug_frobnosticator] = debug_frobnosticator config = Configurator(settings=settings)
199
Its especially important that you mutate the settings dictionary with the converted version of the variable before passing it to the Congurator: the congurator makes a copy of settings, it doesnt use the one you pass directly.
When creating an includeme function that will be later added to your applications conguration you may access the settings dictionary through the instance of the Congurator that is passed into the function as its only argument. For Example:
def includeme(config): settings = config.registry.settings debug_frobnosticator = settings[debug_frobnosticator]
In the runtime code that you need to access the new settings value, nd the value in the registry.settings dictionary and use it. In view code (or any other code that has access to the request), the easiest way to do this is via request.registry.settings. For example:
settings = request.registry.settings debug_frobnosticator = settings[debug_frobnosticator]
If you wish to use the value in code that does not have access to the request and you wish to use the value, youll need to use the pyramid.threadlocal.get_current_registry() API to obtain the current registry, then ask for its settings attribute. For example:
registry = pyramid.threadlocal.get_current_registry() settings = registry.settings debug_frobnosticator = settings[debug_frobnosticator]
200
CHAPTER
EIGHTEEN
LOGGING
Pyramid allows you to make use of the Python standard library logging module. This chapter describes how to congure logging and how to send log messages to loggers that youve congured. This chapter assumes youve used a scaffold to create a project which contains development.ini and production.ini les which help congure logging. All of the scaffolds which ship along with Pyramid do this. If youre not using a scaffold, or if youve used a third-party scaffold which does not create these les, the conguration information in this chapter may not be applicable.
18. LOGGING
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
# Begin logging configuration [loggers] keys = root, {{package_logger}} [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_{{package_logger}}] level = DEBUG handlers = qualname = {{package}} [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 # End logging configuration
The production.ini le uses the WARN level in its logger conguration, but it is otherwise identical. The name {{package_logger}} above will be replaced with the name of your projects package, which is derived from the name you provide to your project. For instance, if you do:
1
202
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
[handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_myapp] level = DEBUG handlers = qualname = myapp [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 # End logging configuration
In this logging conguration: a logger named root is created that logs messages at a level above or equal to the INFO level to stderr, with the following format:
2007-08-17 15:04:08,704 INFO [packagename] Loading resource, id: 86
a logger named myapp is congured that logs messages sent at a level above or equal to DEBUG to stderr in the same format as the root logger. The root logger will be used by all applications in the Pyramid process that ask for a logger (via logging.getLogger) that has a name which begins with anything except your projects package name (e.g. myapp). The logger with the same name as your package name is reserved for your own usage in your Pyramid application. Its existence means that you can log to a known logging location from any Pyramid application generated via a scaffold. Pyramid and many other libraries (such as Beaker, SQLAlchemy, Paste) log a number of messages to the root logger for debugging purposes. Switching the root logger level to DEBUG reveals them: 203
18. LOGGING
Some scaffolds congure additional loggers for additional subsystems they use (such as SQLALchemy). Take a look at the production.ini and development.ini les rendered when you create a project from a scaffold.
import logging log = logging.getLogger(__name__) def myview(request): content_type = text/plain content = Hello World! log.debug(Returning: %s (content-type: %s), content, content_type) request.response.content_type = content_type return request.response
204
No handlers need to be congured for this logger as by default non root loggers will propagate their log records up to their parent loggers handlers. The root logger is the top level parent of all loggers. This technique is used in the default development.ini. The root loggers level is set to INFO, whereas the applications log level is set to DEBUG:
# Begin logging configuration [loggers] keys = root, myapp [logger_myapp] level = DEBUG handlers = qualname = helloworld
All of the child loggers of the myapp logger will inherit the DEBUG level unless theyre explicitly set differently. Meaning the myapp.views, myapp.models (and all your apps modules) loggers by default have an effective level of DEBUG too. For more advanced ltering, the logging module provides a Filter object; however it cannot be used directly from the conguration le. 205
18. LOGGING
These nal 3 lines of conguration directs all of the root loggers output to the myapp.log as well as the console.
206
18.6. REQUEST LOGGING WITH PASTES TRANSLOGGER Like any standard middleware with a Paste entry point, TransLogger can be congured to wrap your application using .ini le syntax. First, rename your Pyramid .ini les [app:main] section to [app:mypyramidapp], then add a [filter:translogger] section, then use a [pipeline:main] section le to form a WSGI pipeline with both the translogger and your application in it. For instance, change from this:
[app:main] use = egg:MyProject
To this:
[app:mypyramidapp] use = egg:MyProject [filter:translogger] use = egg:Paste#translogger setup_console_handler = False [pipeline:main] pipeline = translogger mypyramidapp
Using PasteDeploy this way to form and serve a pipeline is equivalent to wrapping your app in a TransLogger instance via the bottom the main function of your projects __init__ le:
... app = config.make_wsgi_app() from paste.translogger import TransLogger app = TransLogger(app, setup_console_handler=False) return app
TransLogger will automatically setup a logging handler to the console when called with no arguments, so it just works in environments that dont congure logging. Since weve congured our own logging handlers, we need to disable that option via setup_console_handler = False. With the lter in place, TransLoggers logger (named the wsgi logger) will propagate its log messages to the parent logger (the root logger), sending its output to the console when we request a page:
00:50:53,694 INFO [myapp.views] Returning: Hello World! (content-type: text/plain) 00:50:53,695 INFO [wsgi] 192.168.1.111 - - [11/Aug/2011:20:09:33 -0700] "GET /hello HTTP/1.1" 404 - "-" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6"
207
18. LOGGING To direct TransLogger to an access.log FileHandler, we need to add that FileHandler to the wsgi loggers list of handlers:
# Begin logging configuration [loggers] keys = root, myapp, wsgi [logger_wsgi] level = INFO handlers = handler_accesslog qualname = wsgi propagate = 0 [handler_accesslog] class = FileHandler args = (%(here)s/access.log,a) level = INFO formatter = generic
As mentioned above, non-root loggers by default propagate their log records to the root loggers handlers (currently the console handler). Setting propagate to 0 (false) here disables this; so the wsgi logger directs its records only to the accesslog handler. Finally, theres no need to use the generic formatter with TransLogger as TransLogger itself provides all the information we need. Well use a formatter that passes-through the log messages as is:
[formatters] keys = generic, accesslog
208
CHAPTER
NINETEEN
19.1 PasteDeploy
PasteDeploy is the system that Pyramid uses to allow deployment settings to be spelled using an .ini conguration le format. It also allows the pserve command to work. Its conguration format provides a convenient place to dene application deployment settings and WSGI server settings, and its server runner allows you to stop and start a Pyramid application easily. 209
[app:main] use = egg:MyProject 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 [server:main] use = egg:waitress#main host = 0.0.0.0 port = 6543 # Begin logging configuration [loggers] keys = root, myproject [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_myproject] level = DEBUG handlers = qualname = myproject [handler_console] class = StreamHandler args = (sys.stderr,)
210
19.1. PASTEDEPLOY
40 41 42 43 44 45 46
level = NOTSET formatter = generic [formatter_generic] format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s # End logging configuration
The line in [app:main] above that says use = egg:MyProject is actually shorthand for a longer spelling: use = egg:MyProject#main. The #main part is omitted for brevity, as #main is a default dened by PasteDeploy. egg:MyProject#main is a string which has meaning to PasteDeploy. It points at a setuptools entry point named main dened in the MyProject project. Take a look at the generated setup.py le for this project.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
import os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, README.txt)).read() CHANGES = open(os.path.join(here, CHANGES.txt)).read() requires = [ pyramid, pyramid_debugtoolbar, waitress, ] setup(name=MyProject, version=0.0, description=MyProject, long_description=README + \n\n + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author=, author_email=, url=, keywords=web pyramid pylons, packages=find_packages(), include_package_data=True,
211
31 32 33 34 35 36 37 38 39
Note that the entry_point line in setup.py points at a string which looks a lot like an .ini le. This string representation of an .ini le has a section named [paste.app_factory]. Within this section, there is a key named main (the entry point name) which has a value myproject:main. The key main is what our egg:MyProject#main value of the use section in our cong le is pointing at, although it is actually shortened to egg:MyProject there. The value represents a dotted Python name path, which refers to a callable in our myproject packages __init__.py module. The egg: prex in egg:MyProject indicates that this is an entry point URI specier, where the scheme is egg. An egg is created when you run setup.py install or setup.py develop within your project. In English, this entry point can thus be referred to as a PasteDeploy application factory in the MyProject project which has the entry point named main where the entry point refers to a main function in the mypackage module. Indeed, if you open up the __init__.py module generated within any scaffold-generated package, youll see a main function. This is the function called by PasteDeploy when the pserve command is invoked against our application. It accepts a global conguration object and returns an instance of our application.
212
CHAPTER
TWENTY
COMMAND-LINE PYRAMID
Your Pyramid application can be controlled and inspected using a variety of command-line utilities. These utilities are documented in this chapter.
$ ../bin/pviews development.ini#tutorial /FrontPage URL = /FrontPage context: <tutorial.models.Page object at 0xa12536c> view name: View: ----tutorial.views.view_page required permission = view
213
20. COMMAND-LINE PYRAMID The output always has the requested URL at the top and below that all the views that matched with their view conguration details. In this example only one view matches, so there is just a single View section. For each matching view, the full code path to the associated view callable is shown, along with any permissions and predicates that are part of that view conguration. A more complex conguration might generate something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
$ ../bin/pviews development.ini#shootout /about URL = /about context: <shootout.models.RootFactory object at 0xa56668c> view name: about Route: -----route name: about route pattern: /about route path: /about subpath: route predicates (request method = GET) View: ----shootout.views.about_view required permission = view view predicates (request_param testing, header X/header) Route: -----route name: about_post route pattern: /about route path: /about subpath: route predicates (request method = POST) View: ----shootout.views.about_view_post required permission = view view predicates (request_param test) View: ----shootout.views.about_view_post2 required permission = view
214
40
In this case, we are dealing with a URL dispatch application. This specic URL has two matching routes. The matching route information is displayed rst, followed by any views that are associated with that route. As you can see from the second matching route output, a route can be associated with more than one view. For a URL that doesnt match any views, pviews will simply print out a Not found message.
[app:main] use = egg:MyProject pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_templates = true pyramid.default_locale_name = en
If so, you can use the following command to invoke a debug shell using the name main as a section name:
chrism@thinko env26]$ bin/pshell starter/development.ini#main Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32) [GCC 4.4.3] on linux2 Type "help" for more information. Environment: app registry request
215
root Root of the default resource tree. root_factory Default root factory used to create root. >>> root <myproject.resources.MyResource object at 0x445270> >>> registry <Registry myproject> >>> registry.settings[pyramid.debug_notfound] False >>> from myproject.views import my_view >>> from pyramid.request import Request >>> r = Request.blank(/) >>> my_view(r) {project: myproject}
The WSGI application that is loaded will be available in the shell as the app global. Also, if the application that is loaded is the Pyramid app with no surrounding middleware, the root object returned by the default root factory, registry, and request will be available. You can also simply rely on the main default section name by omitting any hash after the lename:
chrism@thinko env26]$ bin/pshell starter/development.ini
216
1 2 3 4 5
By dening the setup callable, we will create the module myapp.lib.pshell containing a callable named setup that will receive the global environment before it is exposed to the shell. Here we mutate the environments request as well as add a new value containing a WebTest version of the application to which we can easily submit requests.
1 2 3 4 5 6 7
# myapp/lib/pshell.py from webtest import TestApp def setup(env): env[request].host = www.example.com env[request].scheme = https env[testapp] = TestApp(env[app])
When this INI le is loaded, the extra variables m, session and t will be available for use immediately. Since a setup callable was also specied, it is executed and a new variable testapp is exposed, and the request is congured to generate urls from the host http://www.example.com. For example:
chrism@thinko env26]$ bin/pshell starter/development.ini Python 2.6.5 (r265:79063, Apr 29 2010, 00:31:32) [GCC 4.4.3] on linux2 Type "help" for more information. Environment: app registry request root root_factory testapp
The WSGI application. Active Pyramid registry. Active request object. Root of the default resource tree. Default root factory used to create root. <webtest.TestApp object at ...>
Custom Variables: m myapp.models session myapp.models.DBSession t transaction >>> testapp.get(/) <200 OK text/html body=<!DOCTYPE...l>\n/3337>
217
[chrism@thinko MyProject]$ ../bin/proutes development.ini Name Pattern View ------------home / <function my_view> home2 / <function my_view> another /another None static/ static/*subpath <static_view object> catchall /*subpath <function static_view>
proutes generates a table. The table has three columns: a Name column, a Pattern column, and a View column. The items listed in the Name column are route names, the items listed in the Pattern column are route patterns, and the items listed in the View column are representations of the view callable that will be invoked when a request matches the associated route pattern. The view column may show None if no associated view callable could be found. If no routes are congured within your application, nothing will be printed to the console when proutes is executed. 218
[chrism@thinko pyramid]$ myenv/bin/ptweens development.ini "pyramid.tweens" config value NOT set (implicitly ordered tweens used) Implicit Tween Chain Position -------0 1 Name ---pyramid_debugtoolbar.toolbar.toolbar_tween_factory pyramid.tweens.excview_tween_factory Alias ----INGRESS pdbt excview MAIN
Heres the ptweens command run against a system congured with explicit tweens dened in its development.ini le:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
[chrism@thinko pyramid]$ ptweens development.ini "pyramid.tweens" config value set (explicitly ordered tweens used) Explicit Tween Chain (used) Position -------0 1 2 Name ---INGRESS starter.tween_factory2 starter.tween_factory1 pyramid.tweens.excview_tween_factory MAIN
219
19 20 21
0 1 -
pyramid_debugtoolbar.toolbar.toolbar_tween_factory pyramid.tweens.excview_tween_factory -
Heres the application conguration section of the development.ini used by the above ptweens command which reports that the explicit tween chain is used:
1 2 3 4 5 6 7 8 9 10 11 12
[app:main] use = egg:starter reload_templates = true debug_authorization = false debug_notfound = false debug_routematch = false debug_templates = true default_locale_name = en pyramid.include = pyramid_debugtoolbar pyramid.tweens = starter.tween_factory2 starter.tween_factory1 pyramid.tweens.excview_tween_factory
220
$ bin/prequest development.ini /
This will print the body of the response to the console on which it was invoked. Several options are supported by prequest. These should precede any cong le name or URL. prequest has a -d (aka --display-headers) option which prints the status and headers returned by the server before the output:
$ bin/prequest -d development.ini /
This will print the status, then the headers, then the body of the response to the console. You can add request header values by using the --header option:
$ bin/prequest --header=Host:example.com development.ini /
Headers are added to the WSGI environment by converting them to their CGI/WSGI equivalents (e.g. Host=example.com will insert the HTTP_HOST header variable as the value example.com). Multiple --header options can be supplied. The special header value content-type sets the CONTENT_TYPE in the WSGI environment. By default, prequest sends a GET request. You can change this by using the -m (aka --method) option. GET, HEAD, POST and DELETE are currently supported. When you use POST, the standard input of the prequest process is used as the POST body:
$ bin/prequest -mPOST development.ini / < somefile
20. COMMAND-LINE PYRAMID environment doesnt have a completely static state independent of a request; your application (and Pyramid itself) is almost always reliant on being able to obtain information from a request. When you run a Python script that simply imports code from your application and tries to run it, there just is no request data, because there isnt any real web request. Therefore some parts of your application and some Pyramid APIs will not work. For this reason, Pyramid makes it possible to run a script in an environment much like the environment produced when a particular request reaches your Pyramid application. This is achieved by using the pyramid.paster.bootstrap() command in the body of your script.
In the simplest case, pyramid.paster.bootstrap() can be used with a single argument, which accepts the PasteDeploy .ini le representing Pyramid your application conguration as a single argument:
from pyramid.paster import bootstrap env = bootstrap(/path/to/my/development.ini) print env[request].route_url(home)
pyramid.paster.bootstrap() returns a dictionary containing framework-related information. This dictionary will always contain a request object as its request key. The following keys are available pyramid.paster.bootstrap(): request A pyramid.request.Request object implying the current request state for your script. app The WSGI application object generated by bootstrapping. root The resource root of your Pyramid application. This is an object generated by the root factory congured in your application. registry 222 in the env dictionary returned by
20.6. WRITING A SCRIPT The application registry of your Pyramid application. closer A parameterless callable that can be used to pop an internal Pyramid threadlocal stack (used by pyramid.threadlocal.get_current_registry() and pyramid.threadlocal.get_current_request()) when your scripting job is nished. Lets assume that the /path/to/my/development.ini le used in the example above looks like so:
[pipeline:main] pipeline = translogger another [filter:translogger] filter_app_factory = egg:Paste#translogger setup_console_handler = False logger_name = wsgi [app:another] use = egg:MyProject
The conguration loaded by the above bootstrap example will use the conguration implied by the [pipeline:main] section of your conguration le by default. Specifying /path/to/my/development.ini is logically equivalent to specifying /path/to/my/development.ini#main. In this case, well be using a conguration that includes an app object which is wrapped in the Paste translogger middleware (which logs requests to the console). You can also specify a particular section of the PasteDeploy .ini le to load instead of main:
from pyramid.paster import bootstrap env = bootstrap(/path/to/my/development.ini#another) print env[request].route_url(home)
The above example species the another app, pipeline, or composite section of your PasteDeploy conguration le. The app object present in the env dictionary returned by pyramid.paster.bootstrap() will be a Pyramid router.
223
You need to inform the Pyramid environment that the WSGI application is handling requests from a certain base. For example, we want to simulate mounting our application at https://example.com/prex, to ensure that the generated URLs are correct for our deployment. This can be done by either mutating the resulting request object, or more simply by constructing the desired request and passing it into bootstrap():
from pyramid.paster import bootstrap from pyramid.request import Request request = Request.blank(/, base_url=https://example.com/prefix) env = bootstrap(/path/to/my/development.ini#another, request=request) print env[request].application_url # will print https://example.com/prefix
Now you can readily use Pyramids APIs for generating URLs:
env[request].route_url(verify, code=1337) # will return https://example.com/prefix/verify/1337
20.6.2 Cleanup
When your scripting logic nishes, its good manners to call the closer callback:
from pyramid.paster import bootstrap env = bootstrap(/path/to/my/development.ini) # .. do stuff ... env[closer]()
224
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
# myproject.scripts module import optparse import sys import textwrap from pyramid.paster import bootstrap def settings_show(): description = """\ Print the deployment settings for a Pyramid application. Example: show_settings deployment.ini """ usage = "usage: %prog config_uri" parser = optparse.OptionParser( usage=usage, description=textwrap.dedent(description) ) parser.add_option( -o, --omit, dest=omit, metavar=PREFIX, type=string, action=append, help=("Omit settings which start with PREFIX (you can use this " "option multiple times)") ) options, args = parser.parse_args(sys.argv[1:]) if not len(args) >= 1: print(You must provide at least one argument) return 2 config_uri = args[0] omit = options.omit if omit is None: omit = [] env = bootstrap(config_uri) settings, closer = env[registry].settings, env[closer] try: for k, v in settings.items(): if any([k.startswith(x) for x in omit]): continue print(%-40s %-20s % (k, v)) finally: closer()
226
20.7. MAKING YOUR SCRIPT INTO A CONSOLE SCRIPT This script uses the Python optparse module to allow us to make sense out of extra arguments passed to the script. It uses the pyramid.paster.bootstrap() function to get information about the application dened by a cong le, and prints the deployment settings dened in that cong le. After adding this script to the package, youll need to tell your distributions setup.py about its existence. Within your distributions top-level directory your setup.py le will look something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
import os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, README.txt)).read() CHANGES = open(os.path.join(here, CHANGES.txt)).read() requires = [pyramid, pyramid_debugtoolbar] setup(name=MyProject, version=0.0, description=My project, long_description=README + \n\n + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pylons", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author=, author_email=, url=, keywords=web pyramid pylons, packages=find_packages(), include_package_data=True, zip_safe=False, install_requires=requires, tests_require=requires, test_suite="myproject", entry_points = """\ [paste.app_factory] main = myproject:main """, )
Were going to change the setup.py le to add an [console_scripts] section with in the entry_points string. Within this section, you should specify a scriptname = dotted.path.to:yourfunction line. For example: 227
The show_settings name will be the name of the script that is installed into bin. The colon (:) between myproject.scripts and settings_show above indicates that myproject.scripts is a Python module, and settings_show is the function in that module which contains the code youd like to run as the result of someone invoking the show_settings script from their command line. The result will be something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
import os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, README.txt)).read() CHANGES = open(os.path.join(here, CHANGES.txt)).read() requires = [pyramid, pyramid_debugtoolbar] setup(name=MyProject, version=0.0, description=My project, long_description=README + \n\n + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pylons", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author=, author_email=, url=, keywords=web pyramid pylons, packages=find_packages(), include_package_data=True, zip_safe=False, install_requires=requires, tests_require=requires, test_suite="myproject", entry_points = """\ [paste.app_factory] main = myproject:main [console_scripts] show_settings = myproject.scripts:settings_show
228
36 37
""", )
Once youve done this, invoking $somevirtualenv/bin/python setup.py develop will install a le named show_settings into the $somevirtualenv/bin directory with a small bit of Python code that points to your entry point. It will be executable. Running it without any arguments will print an error and exit. Running it with a single argument that is the path of a cong le will print the settings. Running it with an --omit=foo argument will omit the settings that have keys that start with foo. Running it with two omit options (e.g. --omit=foo --omit=bar) will omit all settings that have keys that start with either foo or bar:
[chrism@thinko somevenv]$ bin/show_settings development.ini \ --omit=pyramid \ --omit=debugtoolbar debug_routematch False debug_templates True reload_templates True mako.directories [] debug_notfound False default_locale_name en reload_resources False debug_authorization False reload_assets False prevent_http_cache False
Pyramids pserve, pcreate, pshell, prequest, ptweens and other p* scripts are implemented as console scripts. When you invoke one of those, you are using a console script.
229
230
CHAPTER
TWENTYONE
231
1 2
For people more familiar with Zope i18n, a TranslationString is a lot like a zope.i18nmessageid.Message object. It is not a subclass, however. For people more familiar with Pylons or Django i18n, using a TranslationString is a lot like using lazy versions of related gettext APIs. The rst argument to TranslationString is the msgid; it is required. It represents the key into the translation mappings provided by a particular localization. The msgid argument must be a Unicode object or an ASCII string. The msgid may optionally contain replacement markers. For instance:
1 2
Within the string above, ${number} is a replacement marker. It will be replaced by whatever is in the mapping for a translation string. The mapping may be supplied at the same time as the replacement marker itself:
1 2
Any number of replacement markers can be present in the msgid value, any number of times. Only markers which can be replaced by the values in the mapping will be replaced at translation time. The others will not be interpolated and will be output literally. A translation string should also usually carry a domain. The domain represents a translation category to disambiguate it from other translations of the same msgid, in case they conict.
1 2 3
The above translation string named a domain of form. A translator function will often use the domain to locate the right translator le on the lesystem which contains translations for a given domain. In this case, if it were trying to translate our msgid to German, it might try to nd a translation from a gettext le within a translation directory like this one:
232
locale/de/LC_MESSAGES/form.mo
In other words, it would want to take translations from the form.mo translation le in the German language. Finally, the TranslationString constructor accepts a default argument. If a default argument is supplied, it replaces usages of the msgid as the default value for the translation string. When default is None, the msgid value passed to a TranslationString is used as an implicit message identier. Message identiers are matched with translations in translation les, so it is often useful to create translation strings with opaque message identiers unrelated to their default text:
1 2 3
When default text is used, Default text objects may contain replacement values.
We assigned the translation string factory to the name _. This is a convention which will be supported by translation le generation tools.
After assigning _ to the result of a TranslationStringFactory(), the subsequent result of calling _ will be a TranslationString instance. Even though a domain value was not passed to _ (as would have been necessary if the TranslationString constructor were used instead of a translation string factory), the domain attribute of the resulting translation string will be pyramid. As a result, the previous code example is completely equivalent (except for spelling) to:
233
1 2 3
You can set up your own translation string factory much like the one provided above by using the TranslationStringFactory class. For example, if youd like to create a translation string factory which presets the domain value of generated translation strings to form, youd do something like this:
1 2 3
Creating a unique domain for your application via a translation string factory is best practice. Using your own unique translation domain allows another person to reuse your application without needing to merge your translation les with his own. Instead, he can just include your packages translation directory via the pyramid.config.Configurator.add_translation_dirs() method.
For people familiar with Zope internationalization, a TranslationStringFactory is a lot like a zope.i18nmessageid.MessageFactory object. It is not a subclass, however.
The steps a developer must take to work with gettext message catalog les within a Pyramid application are very similar to the steps a Pylons developer must take to do the same. See the Pylons internationalization documentation for more information. GNU gettext uses three types of les in the translation framework, .pot les, .po les and .mo les. .pot (Portable Object Template) les
234
21.2. WORKING WITH GETTEXT TRANSLATION FILES A .pot le is created by a program which searches through your projects source code and which picks out every message identier passed to one of the _() functions (eg. translation string constructions). The list of all message identiers is placed into a .pot le, which serves as a template for creating .po les. .po (Portable Object) les The list of messages in a .pot le are translated by a human to a particular language; the result is saved as a .po le. .mo (Machine Object) les A .po le is turned into a machine-readable binary le, which is the .mo le. Compiling the translations to machine code makes the localized program run faster. The tools for working with gettext translation les related to a Pyramid application is Babel and Lingua. Lingua is a Babel extension that provides support for scraping i18n references out of Python and Chameleon les.
Installation on UNIX If the virtualenv into which youve installed your Pyramid application lives in /my/virtualenv, you can install Babel and Lingua like so:
$ cd /my/virtualenv $ bin/easy_install Babel lingua
Installation on Windows If the virtualenv into which youve installed your Pyramid application lives in C:\my\virtualenv, you can install Babel and Lingua like so:
235
Changing the setup.py You need to add a few boilerplate lines to your applications setup.py le in order to properly generate gettext les from your application.
See Creating a Pyramid Project to learn about the composition of an applications setup.py le. In particular, add the Babel and lingua distributions to the install_requires list and insert a set of references to Babel message extractors within the call to setuptools.setup() inside your applications setup.py le:
1 2 3 4 5 6 7 8 9 10 11 12
setup(name="mypackage", # ... install_requires = [ # ... Babel, lingua, ], message_extractors = { .: [ (**.py, lingua_python, None ), (**.pt, lingua_xml, None ), ]}, )
The message_extractors stanza placed into the setup.py le causes the Babel message catalog extraction machinery to also consider *.pt les when doing message id extraction.
236
Translation Domains The name myapplication above in the lename myapplication.pot denotes the translation domain of the translations that must be performed to localize your application. By default, the translation domain is the project name of your Pyramid application. To change the translation domain of the extracted messages in your project, edit the setup.cfg le of your application, The default setup.cfg le of a pcreate -generated Pyramid application has stanzas in it that look something like the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
[compile_catalog] directory = myproject/locale domain = MyProject statistics = true [extract_messages] add_comments = TRANSLATORS: output_file = myproject/locale/MyProject.pot width = 80 [init_catalog] domain = MyProject input_file = myproject/locale/MyProject.pot output_dir = myproject/locale [update_catalog] domain = MyProject input_file = myproject/locale/MyProject.pot output_dir = myproject/locale previous = true
In the above example, the project name is MyProject. To indicate that youd like the domain of your translations to be mydomain instead, change the setup.cfg le stanzas to look like so:
237
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
[compile_catalog] directory = myproject/locale domain = mydomain statistics = true [extract_messages] add_comments = TRANSLATORS: output_file = myproject/locale/mydomain.pot width = 80 [init_catalog] domain = mydomain input_file = myproject/locale/mydomain.pot output_dir = myproject/locale [update_catalog] domain = mydomain input_file = myproject/locale/mydomain.pot output_dir = myproject/locale previous = true
By default, the message catalog .po le will end up in: myapplication/locale/es/LC_MESSAGES/myapplication.po. Once the le is there, it can be worked on by a human translator. One tool which may help with this is Poedit. Note that Pyramid itself ignores the existence of all .po les. For a running application to have translations available, a .mo le must exist. See Compiling a Message Catalog File. 238
This will create a .mo le for each .po le in your application. As long as the translation directory in which the .mo le ends up in is congured into your application, these translations will be available to Pyramid.
If you need to create a localizer for a locale use the pyramid.i18n.make_localizer() function. 239
from pyramid.i18n import get_localizer from pyramid.i18n import TranslationString ts = TranslationString(Add ${number}, mapping={number:1}, domain=pyramid) def aview(request): localizer = get_localizer(request) translated = localizer.translate(ts) # translation string # ... use translated ...
The get_localizer() function will return a pyramid.i18n.Localizer object bound to the locale name represented by the request. The translation returned from its pyramid.i18n.Localizer.translate() method will depend on the domain attribute of the provided translation string as well as the locale of the localizer. If youre using Chameleon templates, you dont need to pre-translate translation strings this way. See Chameleon Template Support for Translation Strings.
The singular and plural arguments should each be a Unicode value representing a message identier. n should be an integer. domain should be a translation domain, and mapping should be a dictionary that is used for replacement value interpolation of the translated string. If n is plural for the current locale, pluralize will return a Unicode translation for the message id plural, otherwise it will return a Unicode translation for the message id singular. The arguments provided as singular and/or plural may also be translation string objects, but the domain and mapping information attached to those objects is ignored. 240
1 2 3 4 5 6
from pyramid.i18n import get_localizer def aview(request): localizer = get_localizer(request) translated = localizer.pluralize(Item, Items, 1, mydomain) # ... use translated ...
to
request
by
using
the
This returns the locale name negotiated by the currently active locale negotiator or the default locale name if the locale negotiator returns None. You can change the default locale name by changing the pyramid.default_locale_name setting; see Default Locale Name. Once get_locale_name() is rst run, the locale name is stored on the request object. Subsequent calls to get_locale_name() will return the stored locale name without invoking the locale negotiator. To avoid this caching, you can use the pyramid.i18n.negotiate_locale_name() function:
1 2 3 4
You can also obtain the locale name related to a request using the locale_name attribute of a localizer.
1 2 3 4 5
from pyramid.i18n import get_localizer def aview(request): localizer = get_localizer(request) locale_name = localizer.locale_name
Obtaining the locale name as an attribute of a localizer is equivalent to obtaining a locale name by calling the get_locale_name() function. 241
from babel.core import Locale from pyramid.i18n import get_locale_name def aview(request): locale_name = get_locale_name(request) locale = Locale(locale_name)
<span tal:content="some_translation_string"/>
<span tal:replace="some_translation_string"/>
<span>${some_translation_string}</span>
242
21.7. MAKO PYRAMID I18N SUPPORT The features represented by attributes of the i18n namespace of Chameleon will also consult the Pyramid translations. See http://chameleon.repoze.org/docs/latest/i18n.html#the-i18n-namespace. Unlike when Chameleon is used outside of Pyramid, when it is used within Pyramid, it does not support use of the zope.i18n translation framework. Applications which use Pyramid should use the features documented in this chapter rather than zope.i18n. Third party Pyramid template renderers might not provide this support out of the box and may need special code to do an equivalent. For those, you can always use the more manual translation facility described in Performing a Translation.
[app:main] use = egg:MyProject pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.default_locale_name = de
If this value is not supplied via the Congurator constructor or via a cong le, it will default to en. If this setting is supplied within the Pyramid application .ini le, it will be available as a settings key: 243
1 2 3
244
1 2 3
This is only a suggestion. You can create your own available languages conguration scheme as necessary.
A message catalog in a translation directory added via add_translation_dirs() will be merged into translations from a message catalog added earlier if both translation directories contain translations for the same locale and translation domain. 245
If this locale negotiation scheme is inappropriate for a particular application, you can congure a custom locale negotiator function into that application as required. See Using a Custom Locale Negotiator.
21.11. LOCALE NEGOTIATORS First, the negotiator looks for the _LOCALE_ attribute of the request object (possibly set directly by view code or by a listener for an event). Then it looks for the request.params[_LOCALE_] value. Then it looks for the request.cookies[_LOCALE_] value. If no locale can be found via the request, it falls back to using the default locale name (see Localization-Related Deployment Settings). Finally, if the default locale name is not explicitly set, it uses the locale name en.
If a locale negotiator returns None, it signies to Pyramid that the default application locale name should be used. You may add your newly created locale negotiator to your applications conguration by passing an object which can act as the negotiator (or a dotted Python name referring to the object) as the locale_negotiator argument of the Configurator instance during application startup. For example:
1 2
247
1 2 3
248
CHAPTER
TWENTYTWO
VIRTUAL HOSTING
Virtual hosting is, loosely, the act of serving a Pyramid application or a portion of a Pyramid application under a URL space that it does not naturally inhabit. Pyramid provides facilities for serving an application under a URL prex, as well as serving a portion of a traversal based application under a root URL.
249
1 2 3 4 5 6
This roots the Pyramid application at the prex /pyramidapp and serves up the composite as the main application in the le.
If youre using an Apache server to proxy to a Paste urlmap composite, you may have to use the ProxyPreserveHost directive to pass the original HTTP_HOST header along to the application, so URLs get generated properly. As of this writing the urlmap composite does not seem to respect the HTTP_X_FORWARDED_HOST parameter, which will contain the original host header even if HTTP_HOST is incorrect. If you use mod_wsgi, you do not need to use a composite application in your .ini le. The WSGIScriptAlias conguration setting in a mod_wsgi conguration does the work for you:
1
In the above conguration, we root a Pyramid application at /pyramidapp within the Apache conguration.
22.3. FURTHER DOCUMENTATION AND EXAMPLES pyramid.request.Request.resource_url() API will generate the correct virtually-rooted URLs. An example of an Apache mod_proxy conguration that will host the /cms subobject as http://www.example.com/ using this facility is below:
1 2 3 4 5 6 7 8 9
NameVirtualHost *:80 <VirtualHost *:80> ServerName www.example.com RewriteEngine On RewriteRule ^/(.*) http://127.0.0.1:6543/$1 [L,P] ProxyPreserveHost on RequestHeader add X-Vhm-Root /cms </VirtualHost>
Use of the RequestHeader directive requires that the Apache mod_headers module be available in the Apache environment youre using.
For a Pyramid application running under mod_wsgi, the same can be achieved using SetEnv:
1 2 3
Setting a virtual root has no effect when using an application based on URL dispatch.
251
252
CHAPTER
TWENTYTHREE
23. UNIT, INTEGRATION, AND FUNCTIONAL TESTING usually not as easy to do with a set of integration or functional tests, but integration and functional testing provides a measure of assurance that your units work together, as they will be expected to when your application is run in production. The suggested mechanism for unit and integration testing of a Pyramid application is the Python unittest module. Although this module is named unittest, it is actually capable of driving both unit and integration tests. A good unittest tutorial is available within Dive Into Python by Mark Pilgrim. Pyramid provides a number of facilities that make unit, integration, and functional tests easier to write. The facilities become particularly useful when your code calls into Pyramid -related framework functions.
254
1 2 3 4 5 6 7 8 9
import unittest from pyramid import testing class MyTest(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown()
The above will make sure that get_current_registry() called within a test case method of MyTest will return the application registry associated with the config Congurator instance. Each test case method attached to MyTest will use an isolated registry. The setUp() and tearDown() functions accepts various arguments that inuence the environment of the test. See the pyramid.testing chapter for information about the extra arguments supported by these functions. If you also want to make get_current_request() return something other than None during the course of a single test, you can pass a request object into the pyramid.testing.setUp() within the setUp method of your test:
1 2 3 4 5 6 7 8 9 10
import unittest from pyramid import testing class MyTest(unittest.TestCase): def setUp(self): request = testing.DummyRequest() self.config = testing.setUp(request=request) def tearDown(self): testing.tearDown()
If you pass a request object into pyramid.testing.setUp() within your test cases setUp, any test method attached to the MyTest test case that directly or indirectly calls get_current_request() will receive the request object. Otherwise, during testing, get_current_request() will return None. We use a dummy request implementation supplied by pyramid.testing.DummyRequest because its easier to construct than a real Pyramid request object.
255
import unittest class MyTest(unittest.TestCase): def test_my_function(self): from pyramid import testing with testing.testConfig() as config: config.add_route(bar, /bar/{id}) my_function_which_needs_route_bar()
23.1.2 What?
Thread local data structures are always a bit confusing, especially when theyre used by frameworks. Sorry. So heres a rule of thumb: if you dont know whether youre calling code that uses the get_current_registry() or get_current_request() functions, or you dont care about any of this, but you still want to write test code, just always call pyramid.testing.setUp() in your tests setUp method and pyramid.testing.tearDown() in your tests tearDown method. This wont really hurt anything if the application youre testing does not call any get_current* function.
256
1 2 3 4 5 6 7
from pyramid.security import has_permission from pyramid.httpexceptions import HTTPForbidden def view_fn(request): if not has_permission(edit, request.context, request): raise HTTPForbidden return {greeting:hello}
Without doing anything special during a unit test, the call to has_permission() in this view function will always return a True value. When a Pyramid application starts normally, it will populate a application registry using conguration declaration calls made against a Congurator. But if this application registry is not created and populated (e.g. by initializing the congurator with an authorization policy), like when you invoke application code via a unit test, Pyramid API functions will tend to either fail or return default results. So how do you test the branch of the code in this view function that raises HTTPForbidden? The testing API provided by Pyramid allows you to simulate various application registry registrations for use under a unit testing framework without needing to invoke the actual application conguration implied by its main function. For example, if you wanted to test the above view_fn (assuming it lived in the package named my.package), you could write a unittest.TestCase that used the testing API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import unittest from pyramid import testing class MyTest(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def test_view_fn_forbidden(self): from pyramid.httpexceptions import HTTPForbidden from my.package import view_fn self.config.testing_securitypolicy(userid=hank, permissive=False) request = testing.DummyRequest() request.context = testing.DummyResource() self.assertRaises(HTTPForbidden, view_fn, request) def test_view_fn_allowed(self): from my.package import view_fn self.config.testing_securitypolicy(userid=hank, permissive=True)
257
24 25 26 27
In the above example, we create a MyTest test case that inherits from unittest.TestCase. If its in our Pyramid application, it will be found when setup.py test is run. It has two test methods. The rst test method, test_view_fn_forbidden tests the view_fn when the authentication policy forbids the current user the edit permission. Its third line registers a dummy non-permissive authorization policy using the testing_securitypolicy() method, which is a special helper method for unit testing. We then create a pyramid.testing.DummyRequest object which simulates a WebOb request object API. A pyramid.testing.DummyRequest is a request object that requires less setup than a real Pyramid request. We call the function being tested with the manufactured request. When the function is called, pyramid.security.has_permission() will call the dummy authentication policy weve registered through testing_securitypolicy(), which denies access. We check that the view function raises a HTTPForbidden error. The second test method, named test_view_fn_allowed tests the alternate case, where the authentication policy allows access. Notice that we pass different values to testing_securitypolicy() to obtain this result. We assert at the end of this that the view function returns a value. Note that the test calls the pyramid.testing.setUp() function in its setUp method and the pyramid.testing.tearDown() function in its tearDown method. We assign the result of pyramid.testing.setUp() as config on the unittest class. This is a Congurator object and all methods of the congurator can be called as necessary within tests. If you use any of the Configurator APIs during testing, be sure to use this pattern in your test cases setUp and tearDown; these methods make sure youre using a fresh application registry per test run. See the pyramid.testing chapter for the entire Pyramid -specic testing API. This chapter describes APIs for registering a security policy, registering resources at paths, registering event listeners, registering views and view permissions, and classes representing dummy implementations of a request and a resource. See also the various methods of the Congurator documented in pyramid.cong that begin with the testing_ prex.
258
import unittest from pyramid import testing class ViewIntegrationTests(unittest.TestCase): def setUp(self): """ This sets up the application registry with the registrations your application declares in its includeme function. """ import myapp self.config = testing.setUp() self.config.include(myapp) def tearDown(self): """ Clear out the application registry """ testing.tearDown() def test_my_view(self): from myapp.views import my_view request = testing.DummyRequest() result = my_view(request) self.assertEqual(result.status, 200 OK) body = result.app_iter[0] self.failUnless(Welcome to in body)
259
26 27 28 29 30
Unless you cannot avoid it, you should prefer writing unit tests that use the Configurator API to set up the right mock registrations rather than creating an integration test. Unit tests will run faster (because they do less for each test) and the result of a unit test is usually easier to make assertions about.
import unittest class FunctionalTests(unittest.TestCase): def setUp(self): from myapp import main app = main({}) from webtest import TestApp self.testapp = TestApp(app) def test_root(self): res = self.testapp.get(/, status=200) self.failUnless(Pyramid in res.body)
When this test is run, each test creates a real WSGI application using the main function in your myapp.__init__ module and uses WebTest to wrap that WSGI application. It assigns the result to self.testapp. In the test named test_root, we use the testapps get method to invoke the root URL. We then assert that the returned HTML has the string Pyramid in it. See the WebTest documentation for further information about the methods available to a webtest.TestApp instance.
260
CHAPTER
TWENTYFOUR
RESOURCES
A resource is an object that represents a place in a tree related to your application. Every Pyramid application has at least one resource object: the root resource. Even if you dont dene a root resource manually, a default one is created for you. The root resource is the root of a resource tree. A resource tree is a set of nested dictionary-like objects which you can use to represent your websites structure. In an application which uses traversal to map URLs to code, the resource tree structure is used heavily to map each URL to a view callable. When traversal is used, Pyramid will walk through the resource tree by traversing through its nested dictionary structure in order to nd a context resource. Once a context resource is found, the context resource and data in the request will be used to nd a view callable. In an application which uses URL dispatch, the resource tree is only used indirectly, and is often invisible to the developer. In URL dispatch applications, the resource tree is often composed of only the root resource by itself. This root resource sometimes has security declarations attached to it, but is not required to have any. In general, the resource tree is much less important in applications that use URL dispatch than applications that use traversal. In Zope-like Pyramid applications, resource objects also often store data persistently, and offer methods related to mutating that persistent data. In these kinds of applications, resources not only represent the site structure of your website, but they become the domain model of the application. Also: The context and containment predicate arguments to add_view() view_config() decorator) reference a resource class or resource interface. A root factory returns a resource. A resource is exposed to view code as the context of a view. Various helpful Pyramid API methods expect a resource as an argument (e.g. resource_url() and others). 261 (or a
24. RESOURCES
The resource tree weve created above is represented by a dictionary-like root object which has a single child named a. a has a single child named b, and b has a single child named c, which has no children. It is therefore possible to access the c leaf resource like so:
1
root[a][b][c]
If you returned the above root object from a root factory, the path /a/b/c would nd the c object in the resource tree as the result of traversal. In this example, each of the resources in the tree is of the same class. This is not a requirement. Resource elements in the tree can be of any type. We used a single class to represent all resources in the tree for the sake of simplicity, but in a real app, the resources in the tree can be arbitrary. Although the example tree above can service a traversal, the resource instances in the above example are not aware of location, so their utility in a real application is limited. To make best use of built-in Pyramid API facilities, your resources should be location-aware. The next section details how to make resources location-aware. 262
A resource returned from the root resources __getitem__ method should have a __parent__ attribute that is a reference to the root resource, and its __name__ attribute should match the name by which it is reachable via the root resources __getitem__. A container resource within the root resource should have a __getitem__ that returns resources with a __parent__ attribute that points at the container, and these subobjects should have a __name__ attribute that matches the name by which they are retrieved from the container via __getitem__. This pattern continues recursively up the tree from the root. The __parent__ attributes of each resource form a linked list that points downwards toward the root. This is analogous to the .. entry in lesystem directories. If you follow the __parent__ values from any resource in the resource tree, you will eventually come to the root resource, just like if you keep executing the cd .. lesystem command, eventually you will reach the lesystem root directory.
If your root resource has a __name__ argument that is not None or the empty string, URLs returned by the resource_url() function and paths generated by the resource_path() and resource_path_tuple() APIs will be generated improperly. The value of __name__ will be prepended to every path and URL generated (as opposed to a single leading slash or empty tuple element).
263
24. RESOURCES
Using pyramid_traversalwrapper If youd rather not manage the __name__ and __parent__ attributes of your resources by hand, an add-on package named pyramid_traversalwrapper can help. In order to use this helper feature, you must rst install the pyramid_traversalwrapper package (available via PyPI), then register its ModelGraphTraverser as the traversal policy, rather than the default Pyramid traverser. The package contains instructions for doing so. Once Pyramid is congured with this feature, you will no longer need to manage the __parent__ and __name__ attributes on resource objects by hand. Instead, as necessary, during traversal Pyramid will wrap each resource (even the root resource) in a LocationProxy which will dynamically assign a __name__ and a __parent__ to the traversed resource (based on the last traversed resource and the name supplied to __getitem__). The root resource will have a __name__ attribute of None and a __parent__ attribute of None.
Applications which use tree-walking Pyramid APIs require location-aware resources. These APIs include (but are not limited to) resource_url(), find_resource(), find_root(), find_interface(), resource_path(), resource_path_tuple(), or traverse(), virtual_root(), and (usually) has_permission() and principals_allowed_by_permission(). In general, since so much Pyramid infrastructure depends on location-aware resources, its a good idea to make each resource in your tree location-aware.
url = request.resource_url(resource)
The request in the above example is an instance of a Pyramid request object. If the resource referred to as resource in the above example was the root resource, and the host that was used to contact the server was example.com, the URL generated would be 264
24.3. GENERATING THE URL OF A RESOURCE http://example.com/. However, if the resource was a child of the root resource named a, the generated URL would be http://example.com/a/. A slash is appended to all resource URLs when resource_url() is used to generate them in this simple manner, because resources are places in the hierarchy, and URLs are meant to be clicked on to be visited. Relative URLs that you include on HTML pages rendered as the result of the default view of a resource are more apt to be relative to these resources than relative to their parent. You can also pass extra elements to resource_url():
1
If the resource referred to as resource in the above example was the root resource, and the host that was used to contact the server was example.com, the URL generated would be http://example.com/foo/bar. Any number of extra elements can be passed to resource_url() as extra positional arguments. When extra elements are passed, they are appended to the resources URL. A slash is not appended to the nal segment when elements are passed. You can also pass a query string:
1
If the resource referred to as resource in the above example was the root resource, and the host that was used to contact the server was example.com, the URL generated would be http://example.com/?a=1. When a virtual root is active, the URL generated by resource_url() for a resource may be shorter than its physical tree path. See Virtual Root Support for more information about virtually rooting a resource. For more information about generating resource pyramid.request.Request.resource_url(). URLs, see the documentation for
24. RESOURCES physical_path A string representing the physical path computed for the resource, as dened by pyramid.traversal.resource_path(resource). It will begin and end with a slash. virtual_path A string representing the virtual path computed for the resource, as dened by Virtual Root Support. This will be identical to the physical path if virtual rooting is not enabled. It will begin and end with a slash. app_url A string representing the application URL generated during request.resource_url. It will not end with a slash. It represents a potentially customized URL prex, containing potentially custom scheme, host and port information passed by the user to request.resource_url. It should be preferred over use of request.application_url. The __resource_url__ method of a resource should return a string representing a URL. If it cannot override the default, it should return None. If it returns None, the default URL will be returned. Heres an example __resource_url__ method.
1 2 3
The above example actually just generates and returns the default URL, which would have been what was generated by the default resource_url machinery, but your code can perform arbitrary logic as necessary. For example, your code may wish to override the hostname or port number of the generated URL. Note that the URL generated by __resource_url__ should be fully qualied, should end in a slash, and should not contain any query string or anchor elements (only path elements) to work with resource_url().
266
24.5. FINDING A RESOURCE BY PATH If resource in the example above was accessible in the tree as root[a][b], the above example would generate the string /a/b. Any positional arguments passed in to resource_path() will be appended as path segments to the end of the resource path.
1 2
If resource in the example above was accessible in the tree as root[a][b], the above example would generate the string /a/b/foo/bar. The resource passed in must be location-aware. The presence or absence of a virtual root has no impact on the behavior of resource_path().
Or you can resolve a path relative to the resource you pass in by passing a string that isnt prexed by /:
1 2
Often the paths you pass to find_resource() are generated by the resource_path() API. These APIs are mirrors of each other. If the path cannot be resolved when calling find_resource() (if the respective resource in the tree does not exist), a KeyError will be raised. See the pyramid.traversal.find_resource() documentation for more information about resolving a path to a resource. 267
24. RESOURCES
Calling lineage(thing2) will return a generator. When we turn it into a list, we will get:
1 2
The generator returned by lineage() rst returns the resource it was passed unconditionally. Then, if the resource supplied a __parent__ attribute, it returns the resource represented by resource.__parent__. If that resource has a __parent__ attribute, return that resources parent, and so on, until the resource being inspected either has no __parent__ attribute or has a __parent__ attribute of None. See the documentation for pyramid.location.lineage() for more information.
268
24.8. FINDING THE ROOT RESOURCE Calling inside(b, a) will return True, because b has a lineage that includes a. However, calling inside(a, b) will return False because a does not have a lineage that includes b. The argument list for inside() is (resource1, resource2). resource1 is inside resource2 if resource2 is a lineage ancestor of resource1. It is a lineage ancestor if its parent (or one of its parents parents, etc.) is an ancestor. See pyramid.location.inside() for more information.
Calling find_root(b) will return a. The root resource is also available as request.root within view callable code. The presence or absence of a virtual root has no impact on the behavior of find_root(). The root object returned is always the physical root object.
24. RESOURCES
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import datetime from zope.interface import implementer from zope.interface import Interface class IBlogEntry(Interface): pass @implementer(IBlogEntry) class BlogEntry(object): def __init__(self, title, body, author): self.title = title self.body = body self.author = author self.created = datetime.datetime.now()
This resource consists of two things: the class which denes the resource constructor as the class BlogEntry, and an interface attached to the class via an implementer class decorator using the IBlogEntry interface as its sole argument. The interface object used must zope.interface.Interface. be an instance of a class that inherits from
A resource class may implement zero or more interfaces. You specify that a resource implements an interface by using the zope.interface.implementer() function as a class decorator. The above BlogEntry resource implements the IBlogEntry interface. You can also specify that a particular resource instance provides an interface, as opposed to its class. When you declare that a class implements an interface, all instances of that class will also provide that interface. However, you can also just say that a single object provides the interface. To do so, use the zope.interface.directlyProvides() function:
1 2 3 4 5 6 7 8 9 10 11 12 13
import datetime from zope.interface import directlyProvides from zope.interface import Interface class IBlogEntry(Interface): pass class BlogEntry(object): def __init__(self, title, body, author): self.title = title self.body = body self.author = author self.created = datetime.datetime.now()
270
14 15 16
zope.interface.directlyProvides() will replace any existing interface that was previously provided by an instance. If a resource object already has instance-level interface declarations that you dont want to replace, use the zope.interface.alsoProvides() function:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
import datetime from zope.interface import alsoProvides from zope.interface import directlyProvides from zope.interface import Interface class IBlogEntry1(Interface): pass class IBlogEntry2(Interface): pass class BlogEntry(object): def __init__(self, title, body, author): self.title = title self.body = body self.author = author self.created = datetime.datetime.now() entry = BlogEntry(title, body, author) directlyProvides(entry, IBlogEntry1) alsoProvides(entry, IBlogEntry2)
zope.interface.alsoProvides() will augment the set of interfaces directly provided by an instance instead of overwriting them like zope.interface.directlyProvides() does. For more information about how resource interfaces can be used by view conguration, see Using Resource Interfaces In View Conguration.
24. RESOURCES
1 2 3 4 5 6
Calling find_interface(a, Thing1) will return the a resource because a is of class Thing1 (the resource passed as the rst argument is considered rst, and is returned if the class or interface spec matches). Calling find_interface(b, Thing1) will return the a resource because a is of class Thing1 and a is the rst resource in bs lineage of this class. Calling find_interface(b, Thing2) will return the b resource. The second argument to nd_interface may also be a interface instead of a class. If it is an interface, each resource in the lineage is checked to see if the resource implements the speciced interface (instead of seeing if the resource is of a class). See also Resources Which Implement Interfaces.
272
CHAPTER
TWENTYFIVE
25.3. TRAVERSAL (AKA RESOURCE LOCATION) le ended with (for example) .cgi or .php, or if it lived in a special folder, instead of simply sending the le to the client the server would read the le, execute it using an interpreter of some sort, and then send the output from this process to the client as the nal result. The server conguration specied which les would trigger some dynamic code, with the default case being to just serve the static le.
get_root() is some function that returns a root traversal resource. If all of the specied keys exist, then the returned object will be the resource that is being requested, analogous to the JPG le that was retrieved in the le system example. If a KeyError is generated anywhere along the way, Pyramid will return 404. (This isnt precisely true, as youll see when we learn about view lookup below, but the basic idea holds.)
275
Python 2.4.6 (#2, Apr 29 2010, 00:31:48) [GCC 4.4.3] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> adict = {} >>> adict[a] = 1 >>> adict[a] 1 >>> adict.__getitem__(a) 1
The dictionary-like root object stores the ids of all of its subresources as keys, and provides a __getitem__ implementation that fetches them. So get_root() fetches the unique root object, while get_root()[joeschmoe] returns a different object, also stored in the database, which in turn has its own subresources and __getitem__ implementation, etc. These resources might be persisted in a relational database, one of the many NoSQL solutions that are becoming popular these days, or anywhere else, it doesnt matter. As long as the returned objects provide the dictionary-like API (i.e. as long as they have an appropriately implemented __getitem__ method) then traversal will work. In fact, you dont need a database at all. You could use plain dictionaries, with your sites URL structure hard-coded directly in the Python source. Or you could trivially implement a set of objects with __getitem__ methods that search for les in specic directories, and thus precisely recreate the traditional mechanism of having the URL path mapped directly to a folder structure on the le system. Traversal is in fact a superset of le system lookup.
See the chapter entitled Resources for a more technical overview of resources.
276
The get_root and get_view functions dont really exist. Internally, Pyramid does something more complicated. But the example above is a reasonable approximation of the view lookup algorithm in pseudocode.
277
In all of the examples thus far, weve hard coded the typename value, assuming that wed know at development time what names were going to be used (photos, blog, etc.). But what if we dont know what these names will be? Or, worse yet, what if we dont know anything about the structure of the URLs inside a users folder? We could be writing a CMS where we want the end user to be able to arbitrarily add content and other folders inside his folder. He might decide to nest folders dozens of layers deep. How will you construct matching patterns that could account for every possible combination of paths that might develop? It might be possible, but it certainly wont be easy. The matching patterns are going to become complex quickly as you try to handle all of the edge cases. With traversal, however, its straightforward. Twenty layers of nesting would be no problem. Pyramid will happily call __getitem__ as many times as it needs to, until it runs out of path segments or until a resource raises a KeyError. Each resource only needs to know how to fetch its immediate children, the traversal algorithm takes care of the rest. Also, since the structure of the resource tree can live in the database and not in the code, its simple to let users modify the tree at runtime to set up their own personalized directory structures. Another use case in which traversal shines is when there is a need to support a context-dependent security policy. One example might be a document management infrastructure for a large corporation, where members of different departments have varying access levels to the various other departments les. Reasonably, even specic les might need to be made available to specic individuals. Traversal does well here if your resources actually represent the data objects related to your documents, because the idea of a resource authorization is baked right into the code resolution and calling process. Resource objects can store ACLs, which can be inherited and/or overridden by the subresources. If each resource can thus generate a context-based ACL, then whenever view code is attempting to perform a sensitive action, it can check against that ACL to see whether the current user should be allowed to perform the action. In this way you achieve so called instance based or row level security which is considerably harder to model using a traditional tabular approach. Pyramid actively supports such a scheme, and in fact if you register your views with guard permissions and use an authorization policy, Pyramid can check against a resources ACL when deciding whether or not the view itself is available to the current user. 278
25.6. USE CASES In summary, there are entire classes of problems that are more easily served by traversal and view lookup than by URL dispatch. If your problems dont require it, great: stick with URL dispatch. But if youre using Pyramid and you ever nd that you do need to support one of these use cases, youll be glad you have traversal in your toolkit.
It is even possible to mix and match traversal with URL dispatch in the same Pyramid application. See the Combining Traversal and URL Dispatch chapter for details.
279
280
CHAPTER
TWENTYSIX
TRAVERSAL
This chapter explains the technical details of how traversal works in Pyramid. For a quick example, see hellotraversal. For more about why you might use traversal, see Much Ado About Traversal. A traversal uses the URL (Universal Resource Locator) to nd a resource located in a resource tree, which is a set of nested dictionary-like objects. Traversal is done by using each segment of the path portion of the URL to navigate through the resource tree. You might think of this as looking up les and directories in a le system. Traversal walks down the path until it nds a published resource, analogous to a le system directory or le. The resource found as the result of a traversal becomes the context of the request. Then, the view lookup subsystem is used to nd some view code willing to publish this resource by generating a response. Using Traversal to map a URL to code is optional. It is often less easy to understand than URL dispatch, so if youre a rank beginner, it probably makes sense to use URL dispatch to map URLs to code instead of traversal. In that case, you can skip this chapter.
26. TRAVERSAL Traversal treats the PATH_INFO segment of a URL as a sequence of path segments. For example, the PATH_INFO string /a/b/c is converted to the sequence [a, b, c]. This path sequence is then used to descend through the resource tree, looking up a resource for each path segment. Each lookup uses the __getitem__ method of a resource in the tree. For example, if the path info sequence is [a, b, c]: Traversal starts by acquiring the root resource of the application by calling the root factory. The root factory can be congured to return whatever object is appropriate as the traversal root of your application. Next, the rst element (a) is popped from the path segment sequence and is used as a key to lookup the corresponding resource in the root. This invokes the root resources __getitem__ method using that value (a) as an argument. If the root resource contains a resource with key a, its __getitem__ method will return it. The context temporarily becomes the A resource. The next segment (b) is popped from the path sequence, and the A resources __getitem__ is called with that value (b) as an argument; well presume it succeeds. The A resources __getitem__ returns another resource, which well call B. The context temporarily becomes the B resource. Traversal continues until the path segment sequence is exhausted or a path element cannot be resolved to a resource. In either case, the context resource is the last object that the traversal successfully resolved. If any resource found during traversal lacks a __getitem__ method, or if its __getitem__ method raises a KeyError, traversal ends immediately, and that resource becomes the context. The results of a traversal also include a view name. If traversal ends before the path segment sequence is exhausted, the view name is the next remaining path segment element. If the traversal expends all of the path segments, then the view name is the empty string (). The combination of the context resource and the view name found via traversal is used later in the same request by the view lookup subsystem to nd a view callable. How Pyramid performs view lookup is explained within the View Conguration chapter.
282
Heres an example of using this root factory within startup conguration, by passing it to an instance of a Congurator named config:
1
config = Configurator(root_factory=Root)
The root_factory argument to the Configurator constructor registers this root factory to be called to generate a root resource whenever a request enters the application. The root factory registered this way is also known as the global root factory. A root factory can alternately be passed to the Configurator as a dotted Python name which can refer to a root factory dened in a different module. If no root factory is passed to the Pyramid Congurator constructor, or if the root_factory value specied is None, a default root factory is used. The default root factory always returns a resource that has no child resources; it is effectively empty. Usually a root factory for a traversal-based application will be more complicated than the above Root class; in particular it may be associated with a database connection or another persistence mechanism.
283
26. TRAVERSAL
Emulating the Default Root Factory For purposes of understanding the default root factory better, well note that you can emulate the default root factory by using this code as an explicit root factory in your application setup:
1 2 3 4 5
The default root factory is just a really stupid object that has no behavior or state. Using traversal against an application that uses the resource tree supplied by the default root resource is not very interesting, because the default root resource has no children. Its availability is more useful when youre developing an application using URL dispatch.
If the items contained within the resource tree are persistent (they have state that lasts longer than the execution of a single process), they become analogous to the concept of domain model objects used by many other frameworks. The resource tree consists of container resources and leaf resources. There is only one difference between a container resource and a leaf resource: container resources possess a __getitem__ method (making it dictionary-like) while leaf resources do not. The __getitem__ method was chosen as the signifying difference between the two types of resources because the presence of this method is how Python itself typically determines whether an object is containerish or not (dictionary objects are containerish). Each container resource is presumed to be willing to return a child resource or raise a KeyError based on a name passed to its __getitem__. Leaf-level instances must not have a __getitem__. If instances that youd like to be leaves already happen to have a __getitem__ through some historical inequity, you should subclass these resource types and cause their __getitem__ methods to simply raise a KeyError. Or just disuse them and think up another strategy. Usually, the traversal root is a container resource, and as such it contains other resources. However, it doesnt need to be a container. Your resource tree can be as shallow or as deep as you require. In general, the resource tree is traversed beginning at its root resource using a sequence of path elements described by the PATH_INFO of the current request; if there are path segments, the root resources __getitem__ is called with the next path segment, and it is expected to return another resource. The resulting resources __getitem__ is called with the very next path segment, and it is expected to return another resource. This happens ad innitum until all path segments are exhausted. 284
285
26. TRAVERSAL 5. Traversal begins at the root resource returned by the root factory. For the traversal sequence [ua, ub, uc], the root resources __getitem__ is called with the name a. Traversal continues through the sequence. In our example, if the root resources __getitem__ called with the name a returns a resource (aka resource A), that resources __getitem__ is called with the name b. If resource A returns a resource B when asked for b, resource Bs __getitem__ is then asked for the name c, and may return resource C. 6. Traversal ends when a) the entire path is exhausted or b) when any resouce raises a KeyError from its __getitem__ or c) when any non-nal path element traversal does not have a __getitem__ method (resulting in a AttributeError) or d) when any path element is prexed with the set of characters @@ (indicating that the characters following the @@ token should be treated as a view name). 7. When traversal ends for any of the reasons in the previous step, the last resource found during traversal is deemed to be the context. If the path has been exhausted when traversal ends, the view name is deemed to be the empty string (). However, if the path was not exhausted before traversal terminated, the rst remaining path segment is treated as the view name. 8. Any subsequent path elements after the view name is found are deemed the subpath. The subpath is always a sequence of path segments that come from PATH_INFO that are left over after traversal has completed. Once the context resource, the view name, and associated attributes such as the subpath are located, the job of traversal is nished. It passes back the information it obtained to its caller, the Pyramid Router, which subsequently invokes view lookup with the context and view name information. The traversal algorithm exposes two special cases: You will often end up with a view name that is the empty string as the result of a particular traversal. This indicates that the view lookup machinery should look up the default view. The default view is a view that is registered with no name or a view which is registered with a name that equals the empty string. If any path segment element begins with the special characters @@ (think of them as goggles), the value of that segment minus the goggle characters is considered the view name immediately and traversal stops there. This allows you to address views that may have the same names as resource names in the tree unambiguously. Finally, traversal is responsible for locating a virtual root. A virtual root is used during virtual hosting; see the Virtual Hosting chapter for information. We wont speak more about it in this chapter.
286
287
26. TRAVERSAL
Heres what happens: traversal traverses the root, and attempts to nd foo, which it nds. traversal traverses foo, and attempts to nd bar, which it nds. traversal traverses bar, and attempts to nd baz, which it does not nd (the bar resource raises a KeyError when asked for baz). The fact that it does not nd baz at this point does not signify an error condition. It signies that: the context is the bar resource (the context is the last resource found during traversal). the view name is baz the subpath is (biz, buz.txt) At this point, traversal has ended, and view lookup begins. Because its the context resource, the view lookup machinery examines bar to nd out what type it is. Lets say it nds that the context is a Bar type (because bar happens to be an instance of the class Bar). Using the view name (baz) and the type, view lookup asks the application registry this question: Please nd me a view callable registered using a view conguration with the name baz that can be used for the class Bar. Lets say that view lookup nds no matching view type. In this circumstance, the Pyramid router returns the result of the not found view and the request ends. However, for this tree: 288
The user asks for http://example.com/foo/bar/baz/biz/buz.txt traversal traverses foo, and attempts to nd bar, which it nds. traversal traverses bar, and attempts to nd baz, which it nds. traversal traverses baz, and attempts to nd biz, which it nds. traversal traverses biz, and attempts to nd buz.txt which it does not nd. The fact that it does not nd a resource related to buz.txt at this point does not signify an error condition. It signies that: the context is the biz resource (the context is the last resource found during traversal). the view name is buz.txt the subpath is an empty sequence ( () ). At this point, traversal has ended, and view lookup begins. Because its the context resource, the view lookup machinery examines the biz resource to nd out what type it is. Lets say it nds that the resource is a Biz type (because biz is an instance of the Python class Biz). Using the view name (buz.txt) and the type, view lookup asks the application registry this question: Please nd me a view callable registered with a view conguration with the name buz.txt that can be used for class Biz. Lets say that question is answered by the application registry; in such a situation, the application registry returns a view callable. The view callable is then called with the current WebOb request as the sole argument: request; it is expected to return a response. 289
26. TRAVERSAL
The Example View Callables Accept Only a Request; How Do I Access the Context Resource? Most of the examples in this book assume that a view callable is typically passed only a request object. Sometimes your view callables need access to the context resource, especially when you use traversal. You might use a supported alternate view callable argument list in your view callables such as the (context, request) calling convention described in Alternate View Callable Argument/Calling Conventions. But you dont need to if you dont want to. In view callables that accept only a request, the context resource found by traversal is available as the context attribute of the request object, e.g. request.context. The view name is available as the view_name attribute of the request object, e.g. request.view_name. Other Pyramid -specic request attributes are also available as described in Special Attributes Added to the Request by Pyramid.
the
from zope.interface import Interface from zope.interface import implementer class IHello(Interface): """ A marker interface """ @implementer(IHello) class Hello(object): pass
To attach an interface to a resource instance, you dene the interface and use the zope.interface.alsoProvides() function to associate the interface with the instance. This function mutates the instance in such a way that the interface is attached to it. 290
1 2 3 4 5 6 7 8 9 10 11 12 13
from zope.interface import Interface from zope.interface import alsoProvides class IHello(Interface): """ A marker interface """ class Hello(object): pass def make_hello(): hello = Hello() alsoProvides(hello, IHello) return hello
Regardless of how you associate an interface, with a resource instance, or a resource class, the resulting code to associate that interface with a view callable is the same. Assuming the above code that denes an IHello interface lives in the root of your application, and its module is named resources.py, the interface declaration below will associate the mypackage.views.hello_world view with resources that implement, or provide, this interface.
1 2 3 4
Any time a resource that is determined to be the context provides this interface, and a view named hello.html is looked up against it as per the URL, the mypackage.views.hello_world view callable will be invoked. Note, in cases where a view is registered against a resource class, and a view is also registered against an interface that the resource class implements, an ambiguity arises. Views registered for the resource class take precedence over any views registered for any interface the resource class implements. Thus, if one view conguration names a context of both the class type of a resource, and another view conguration names a context of interface implemented by the resources class, and both view congurations are otherwise identical, the view registered for the contexts class will win. For more information about dening resources with interfaces for use within view conguration, see Resources Which Implement Interfaces.
291
26. TRAVERSAL
26.4 References
A tutorial showing how traversal can be used within a Pyramid application exists in ZODB + Traversal Wiki Tutorial. See the View Conguration chapter for detailed information about view lookup. The pyramid.traversal module contains API functions that deal with traversal, such as traversal invocation from within application code. The pyramid.request.Request.resource_url() method generates a URL when given a resource retrieved from a resource tree.
292
CHAPTER
TWENTYSEVEN
SECURITY
Pyramid provides an optional declarative authorization system that can prevent a view from being invoked based on an authorization policy. Before a view is invoked, the authorization system can use the credentials in the request along with the context resource to determine if access will be allowed. Heres how it works at a high level: A request is generated when a user visits the application. Based on the request, a context resource is located through resource location. A context is located differently depending on whether the application uses traversal or URL dispatch, but a context is ultimately found in either case. See the URL Dispatch chapter for more information. A view callable is located by view lookup using the context as well as other attributes of the request. If an authentication policy is in effect, it is passed the request; it returns some number of principal identiers. If an authorization policy is in effect and the view conguration associated with the view callable that was found has a permission associated with it, the authorization policy is passed the context, some number of principal identiers returned by the authentication policy, and the permission associated with the view; it will allow or deny access. If the authorization policy allows access, the view callable is invoked. If the authorization policy denies access, the view callable is not invoked; instead the forbidden view is invoked. Security in Pyramid, unlike many systems, cleanly and explicitly separates authentication and authorization. Authentication is merely the mechanism by which credentials provided in the request are resolved to one or more principal identiers. These identiers represent the users and groups in effect during the request. Authorization then determines access based on the principal identiers, the view callable being invoked, and the context resource. Authorization is enabled by modifying your application to include an authentication policy and authorization policy. Pyramid comes with a variety of implementations of these policies. To provide maximal exibility, Pyramid also allows you to create custom authentication policies and authorization policies. 293
27. SECURITY
from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy authn_policy = AuthTktAuthenticationPolicy(seekrit, hashalg=sha512) authz_policy = ACLAuthorizationPolicy() config = Configurator() config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy)
the authentication_policy and authorization_policy arguments may also be passed to their respective methods mentioned above as dotted Python name values, each representing the dotted name path to a suitable implementation global dened at Python module scope. The above conguration enables a policy which compares the value of an auth ticket cookie passed in the requests environment which contains a reference to a single principal against the principals present in any ACL found in the resource tree when attempting to call some view. While it is possible to mix and match different authentication and authorization policies, it is an error to congure a Pyramid application with an authentication policy but without the authorization policy or vice versa. If you do this, youll receive an error at application startup time. See also the pyramid.authorization and pyramid.authentication modules for alternate implementations of authorization and authentication policies. 294
The equivalent view registration including the add permission name may be performed via the @view_config decorator:
1 2 3 4 5 6 7
from pyramid.view import view_config from 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
As a result of any of these various view conguration statements, if an authorization policy is in place when the view callable is found during normal application operations, the requesting user will need to possess the add permission against the context resource in order to be able to invoke the blog_entry_add_view view. If he does not, the Forbidden view will be invoked.
27. SECURITY The pyramid.config.Configurator.set_default_permission() method supports conguring a default permission for an application. When a default permission is registered: If a view conguration names an explicit permission, the default permission is ignored for that view registration, and the view-conguration-named permission is used. If a view conguration names the permission pyramid.security.NO_PERMISSION_REQUIRED, the default permission is ignored, and the view is registered without a permission (making it available to all callers regardless of their credentials).
When you register a default permission, all views (even exception view views) are protected by a permission. For all views which are truly meant to be anonymously accessible, you will need to associate the views conguration with the pyramid.security.NO_PERMISSION_REQUIRED permission.
from pyramid.security import Everyone from pyramid.security import Allow class Blog(object): __acl__ = [ (Allow, Everyone, view), (Allow, group:editors, add), (Allow, group:editors, edit), ]
Or, if your resources are persistent, an ACL might be specied via the __acl__ attribute of an instance of a resource:
296
1 2 3 4 5 6 7 8 9 10 11 12 13
from pyramid.security import Everyone from pyramid.security import Allow class Blog(object): pass blog = Blog() blog.__acl__ = [ (Allow, Everyone, view), (Allow, group:editors, add), (Allow, group:editors, edit), ]
Whether an ACL is attached to a resources class or an instance of the resource itself, the effect is the same. It is useful to decorate individual resource instances with an ACL (as opposed to just decorating their class) in applications such as CMS systems where ne-grained access is required on an object-byobject basis.
from pyramid.security import Everyone from pyramid.security import Allow __acl__ = [ (Allow, Everyone, view), (Allow, group:editors, add), (Allow, group:editors, edit), ]
The example ACL indicates that the pyramid.security.Everyone principal a special systemdened principal indicating, literally, everyone is allowed to view the blog, the group:editors principal is allowed to add to and edit the blog. Each element of an ACL is an ACE or access control entry. For example, in the above code block, there are three ACEs: (Allow, Everyone, view), (Allow, group:editors, add), and (Allow, group:editors, edit).
297
27. SECURITY The rst element of any ACE is either pyramid.security.Allow, or pyramid.security.Deny, representing the action to take when the ACE matches. The second element is a principal. The third argument is a permission or sequence of permission names. A principal is usually a user id, however it also may be a group id if your authentication system provides group information and the effective authentication policy policy is written to respect group information. For example, the pyramid.authentication.RepozeWho1AuthenicationPolicy respects group information if you congure it with a callback. Each ACE in an ACL is processed by an authorization policy in the order dictated by the ACL. So if you have an ACL like this:
1 2 3 4 5 6 7 8
from pyramid.security import Everyone from pyramid.security import Allow from pyramid.security import Deny __acl__ = [ (Allow, Everyone, view), (Deny, Everyone, view), ]
The default authorization policy will allow everyone the view permission, even though later in the ACL you have an ACE that denies everyone the view permission. On the other hand, if you have an ACL like this:
1 2 3 4 5 6 7 8
from pyramid.security import Everyone from pyramid.security import Allow from pyramid.security import Deny __acl__ = [ (Deny, Everyone, view), (Allow, Everyone, view), ]
The authorization policy will deny everyone the view permission, even though later in the ACL is an ACE that allows everyone. The third argument in an ACE can also be a sequence of permission names instead of a single permission name. So instead of creating multiple ACEs representing a number of different permission grants to a single group:editors group, we can collapse this into a single ACE, as below.
298
1 2 3 4 5 6 7
from pyramid.security import Everyone from pyramid.security import Allow __acl__ = [ (Allow, Everyone, view), (Allow, group:editors, (add, edit)), ]
299
27. SECURITY
from pyramid.security import Allow from pyramid.security import DENY_ALL __acl__ = [ (Allow, fred, view), DENY_ALL ]
An object with a __parent__ attribute and a __name__ attribute is said to be location-aware. Location-aware objects dene an __parent__ attribute which points at their parent object. The root objects __parent__ is None. See pyramid.location for documentations of functions which use location-awareness. See also LocationAware Resources. 300
When any authorization takes place during a top-level view rendering, a message will be logged to the console (to stderr) about what ACE in which ACL permitted or denied the authorization based on authentication information. This behavior can also be turned on in the application .ini le by setting the pyramid.debug_authorization key to true within the applications conguration section, e.g.:
1 2 3
With this debug ag turned on, the response sent to the browser will also contain security debugging information in its body.
27. SECURITY
class IAuthenticationPolicy(object): """ An object representing a Pyramid authentication policy. """ def authenticated_userid(self, request): """ Return the authenticated userid or None if no authenticated userid can be found. This method of the policy should ensure that a record exists in whatever persistent store is used related to the user (the user should not have been deleted); if a record associated with the current id does not exist in a persistent store, it should return None.""" def unauthenticated_userid(self, request): """ Return the *unauthenticated* userid. This method performs the same duty as authenticated_userid but is permitted to return the userid based only on data present in the request; it neednt (and shouldnt) check any persistent store to ensure that the user record related to the request userid exists.""" def effective_principals(self, request): """ Return a sequence representing the effective principals including the userid and any groups belonged to by the current user, including system groups such as pyramid.security.Everyone and pyramid.security.Authenticated. """ def remember(self, request, principal, **kw): """ Return a set of headers suitable for remembering the principal named principal when set in a response. An individual authentication policy and its consumers can decide on the composition and meaning of **kw. """ def forget(self, request): """ Return a set of headers suitable for forgetting the current user on subsequent requests. """
After you do so, you can pass an instance of such a class into the set_authentication_policy method conguration time to use it. 302
However, in some cases, its useful to be able to use a different authorization policy than the default ACLAuthorizationPolicy. For example, it might be desirable to construct an alternate authorization policy which allows the application to use an authorization mechanism that does not involve ACL objects. Pyramid ships with only a single default authorization policy, so youll need to create your own if youd like to use a different one. Creating and using your own authorization policy is a matter of creating an instance of an object that implements the following interface:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
class IAuthorizationPolicy(object): """ An object representing a Pyramid authorization policy. """ def permits(self, context, principals, permission): """ Return True if any of the principals is allowed the permission in the current context, else return False """ def principals_allowed_by_permission(self, context, permission): """ Return a set of principal identifiers allowed by the permission in context. This behavior is optional; if you choose to not implement it you should define this method as something which raises a NotImplementedError. This method will only be called when the pyramid.security.principals_allowed_by_permission API is used."""
After you do so, you can pass an instance of such a class into the set_authorization_policy method at conguration time to use it.
303
27. SECURITY
304
CHAPTER
TWENTYEIGHT
1 2 3 4 5 6 7
# config is an instance of pyramid.config.Configurator config.add_route(foobar, {foo}/{bar}) config.add_route(bazbuz, {baz}/{buz}) config.add_view(myproject.views.foobar, route_name=foobar) config.add_view(myproject.views.bazbuz, route_name=bazbuz)
Each route corresponds to one or more view callables. Each view callable is associated with a route by passing a route_name parameter that matches its name during a call to add_view(). When a route is matched during a request, view lookup is used to match the request to its associated view callable. The presence of calls to add_route() signify that an application is using URL dispatch.
When the above conguration is applied to an application, the mypackage.views.foobar view callable above will be called when the URL /foobar is visited. Likewise, the view mypackage.views.bazbuz will be called when the URL /bazbuz is visited. Typically, an application that uses traversal exclusively wont perform pyramid.config.Configurator.add_route() in its startup code. any calls to
28.2. HYBRID APPLICATIONS pattern that represents the pattern argument of a route must match the PATH_INFO of a request, and after the route pattern has matched, most of the normal rules of traversal with respect to resource location and view lookup apply. There are only four real differences between a purely traversal-based application and a hybrid application: In a purely traversal based application, no routes are dened; in a hybrid application, at least one route will be dened. In a purely traversal based application, the root object used is global, implied by the root factory provided at startup time; in a hybrid application, the root object at which traversal begins may be varied on a per-route basis. In a purely traversal-based application, the PATH_INFO of the underlying WSGI environment is used wholesale as a traversal path; in a hybrid application, the traversal path is not the entire PATH_INFO string, but a portion of the URL determined by a matching pattern in the matched route congurations pattern. In a purely traversal based application, view congurations which do not mention a route_name argument are considered during view lookup; in a hybrid application, when a route is matched, only view congurations which mention that routes name as a route_name are considered during view lookup. More generally, a hybrid application is a traversal-based application except: the traversal root is chosen based on the route conguration of the route that matched instead of from the root_factory supplied during application startup conguration. the traversal path is chosen based on the route conguration of the route that matched rather than from the PATH_INFO of a request. the set of views that may be chosen during view lookup when a route matches are limited to those which specically name a route_name in their conguration that is the same as the matched routes name. To create a hybrid mode application, use a route conguration that implies a particular root factory and which also includes a pattern argument that contains a special dynamic part: either *traverse or *subpath.
307
Root factories related to a route were explained previously within Route Factories. Both the global root factory and default root factory were explained previously within The Resource Tree.
config.add_route(home, {foo}/{bar}/*traverse)
A *traverse token at the end of the pattern in a routes conguration implies a remainder capture value. When it is used, it will match the remainder of the path segments of the URL. This remainder becomes the path used to perform traversal.
The *remainder route pattern syntax is explained in more detail within Route Pattern Syntax. A hybrid mode application relies more heavily on traversal to do resource location and view lookup than most examples indicate within URL Dispatch. 308
28.2. HYBRID APPLICATIONS Because the pattern of the above route ends with *traverse, when this route conguration is matched during a request, Pyramid will attempt to use traversal against the root object implied by the root factory that is implied by the routes conguration. Since no root_factory argument is explicitly specied for this route, this will either be the global root factory for the application, or the default root factory. Once traversal has found a context resource, view lookup will be invoked in almost exactly the same way it would have been invoked in a pure traversal-based application. Lets assume there is no global root factory congured in this application. The default root factory cannot be traversed: it has no useful __getitem__ method. So well need to associate this route conguration with a custom root factory in order to create a useful hybrid application. To that end, lets imagine that weve created a root factory that looks like so in a module named routes.py:
1 2 3 4 5 6 7 8 9 10 11 12 13
class Resource(object): def __init__(self, subobjects): self.subobjects = subobjects def __getitem__(self, name): return self.subobjects[name] root = Resource( {a: Resource({b: Resource({c: Resource({})})})} ) def root_factory(request): return root
Above, weve dened a (bogus) resource tree that can be traversed, and a root_factory function that can be used as part of a particular route conguration statement:
1 2
The factory above points at the function weve dened. It will return an instance of the Resource class as a root object whenever this route is matched. Instances of the Resource class can be used for tree traversal because they have a __getitem__ method that does something nominally useful. Since traversal uses __getitem__ to walk the resources of a resource tree, using traversal against the root resource implied by our route statement is a reasonable thing to do.
We could have also used our root_factory function as the root_factory argument of the Configurator constructor, instead of associating it with a particular route inside the routes conguration. Every hybrid route conguration that is matched but which does not name a factory attribute will use the use global root_factory function to generate a root object.
309
28. COMBINING TRAVERSAL AND URL DISPATCH When the route conguration named home above is matched during a request, the matchdict generated will be based on its pattern: {foo}/{bar}/*traverse. The capture value implied by the *traverse element in the pattern will be used to traverse the resource tree in order to nd a context resource, starting from the root object returned from the root factory. In the above example, the root object found will be the instance named root in routes.py. If the URL that matched a route with the pattern {foo}/{bar}/*traverse, is http://example.com/one/two/a/b/c, the traversal path used against the root object will be a/b/c. As a result, Pyramid will attempt to traverse through the edges a, b, and c, beginning at the root object. In our above example, this particular set of traversal steps will mean that the context resource of the view would be the Resource object weve named c in our bogus resource tree and the view name resulting from traversal will be the empty string; if you need a refresher about why this outcome is presumed, see The Traversal Algorithm. At this point, a suitable view callable will be found and invoked using view lookup as described in View Conguration, but with a caveat: in order for view lookup to work, we need to dene a view conguration that will match when view lookup is invoked after a route matches:
1 2 3
Note that the above call to add_view() includes a route_name argument. View congurations that include a route_name argument are meant to associate a particular view declaration with a route, using the routes name, in order to indicate that the view should only be invoked when the route matches. Calls to add_view() may pass a route_name attribute, which refers to the value of an existing routes name argument. In the above example, the route name is home, referring to the name of the route dened above it. The above mypackage.views.myview view callable will be invoked when: the route named home is matched the view name resulting from traversal is the empty string. the context resource is any object. It is also possible to declare alternate views that may be invoked when a hybrid route is matched:
310
1 2 3 4 5
The add_view call for mypackage.views.another_view above names a different view and, more importantly, a different view name. The above mypackage.views.another_view view will be invoked when: the route named home is matched the view name resulting from traversal is another. the context resource is any object. For instance, if the URL http://example.com/one/two/a/another is provided to an application that uses the previously mentioned resource tree, the mypackage.views.another view callable will be called instead of the mypackage.views.myview view callable because the view name will be another instead of the empty string. More complicated matching can be composed. All arguments to route conguration statements and view conguration statements are supported in hybrid applications (such as predicate arguments).
311
28. COMBINING TRAVERSAL AND URL DISPATCH The syntax of the traverse argument is the same as it is for pattern. If, as above, the pattern provided is /articles/{article}/edit, and the traverse argument provided is /{article}, when a request comes in that causes the route to match in such a way that the article match value is 1 (when the request URI is /articles/1/edit), the traversal path will be generated as /1. This means that the root objects __getitem__ will be called with the name 1 during the traversal phase. If the 1 object exists, it will become the context of the request. The Traversal chapter has more information about traversal. If the traversal path contains segment marker names which are not present in the pattern argument, a runtime error will occur. The traverse pattern should not contain segment markers that do not exist in the path. Note that the traverse argument is ignored when attached to a route that has a *traverse remainder marker in its pattern. Traversal will begin at the root object implied by this route (either the global root, or the object returned by the factory associated with this route). Making Global Views Match By default, only view congurations that mention a route_name will be found during view lookup when a route that has a *traverse in its pattern matches. You can allow views without a route_name attribute to match a route by adding the use_global_views ag to the route denition. For example, the myproject.views.bazbuz view below will be found if the route named abc below is matched and the PATH_INFO is /abc/bazbuz, even though the view conguration statement does not have the route_name="abc" attribute.
1 2
1 2 3 4 5 6
from pryamid.static import static_view www = static_view(mypackage:static, use_subpath=True) config.add_route(static, /static/*subpath) config.add_view(www, route_name=static)
mypackage.views.www is an instance of pyramid.static.static_view. This effectively tells the static helper to traverse everything in the subpath as a lename.
28.3.1 Registering a Default View for a Route That Has a view Attribute
As of Pyramid 1.1 this section is slated to be removed in a later documentation release because the ability to add views directly to the route conguration by passing a view argument to add_route has been deprecated. It is an error to provide both a view argument to a route conguration and a view conguration which names a route_name that has no name value or the empty name value. For example, this pair of declarations will generate a conict error at startup time.
1 2 3
This is because the view argument to the add_route() above is an implicit default view when that route matches. add_route calls dont need to supply a view attribute. For example, this add_route call:
1 2
1 2
The two spellings are logically equivalent. In fact, the former is just a syntactical shortcut for the latter.
28.3.2 Binding Extra Views Against a Route Conguration that Doesnt Have a *traverse Element In Its Pattern
Heres another corner case that just makes no sense:
1 2 3
The above view declaration is useless, because it will never be matched when the route it references has matched. Only the view associated with the route itself (myproject.views.abc) will ever be invoked when the route matches, because the default view is always invoked when a route matches and when no post-match traversal is performed. To make the above view declaration useful, the special *traverse token must end the routes pattern. For example:
1 2 3
With the above conguration, the myproject.views.bazbuz view will be invoked when the request URI is /abc/bazbuz, assuming there is no object contained by the root object with the key bazbuz. A different request URI, such as /abc/foo/bar, would invoke the default myproject.views.abc view.
314
CHAPTER
TWENTYNINE
INVOKING A SUBREQUEST
This feature was added in Pyramid 1.4a1. Pyramid allows you to invoke a subrequest at any point during the processing of a request. Invoking a subrequest allows you to obtain a response object from a view callable within your Pyramid application while youre executing a different view callable within the same application. Heres an example application which uses a subrequest:
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.request import Request def view_one(request): subreq = Request.blank(/view_two) response = request.invoke_subrequest(subreq) return response def view_two(request): request.response.body = This came from view_two return request.response if __name__ == __main__: config = Configurator() config.add_route(one, /view_one) config.add_route(two, /view_two) config.add_view(view_one, route_name=one) config.add_view(view_two, route_name=two) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
315
29. INVOKING A SUBREQUEST When /view_one is visted in a browser, the text printed in the browser pane will be This came from view_two. The view_one view used the pyramid.request.Request.invoke_subrequest() API to obtain a response from another view (view_two) within the same application when it executed. It did so by constructing a new request that had a URL that it knew would match the view_two view registration, and passed that new request along to pyramid.request.Request.invoke_subrequest(). The view_two view callable was invoked, and it returned a response. The view_one view callable then simply returned the response it obtained from the view_two view callable. Note that it doesnt matter if the view callable invoked via a subrequest actually returns a literal Response object. Any view callable that uses a renderer or which returns an object that can be interpreted by a response adapter when found and invoked via pyramid.request.Request.invoke_subrequest() will return a Response object:
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.request import Request def view_one(request): subreq = Request.blank(/view_two) response = request.invoke_subrequest(subreq) return response def view_two(request): return This came from view_two if __name__ == __main__: config = Configurator() config.add_route(one, /view_one) config.add_route(two, /view_two) config.add_view(view_one, route_name=one) config.add_view(view_two, route_name=two, renderer=string) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
Even though the view_two view callable returned a string, it was invoked in such a way that the string renderer associated with the view registration that was found turned it into a real response object for consumption by view_one. Being able to unconditionally obtain a response object by invoking a view callable indirectly is the main advantage to using pyramid.request.Request.invoke_subrequest() instead of simply importing a view callable and executing it directly. Note that theres not much advantage to invoking a view
316
using a subrequest if you can invoke a view callable directly. Subrequests are slower and are less convenient if you actually do want just the literal information returned by a function that happens to be a view callable. Note that, by default, if a view callable invoked by a subrequest raises an exception, the exception will be raised to the caller of invoke_subrequest() even if you have a exception view congured:
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.request import Request def view_one(request): subreq = Request.blank(/view_two) response = request.invoke_subrequest(subreq) return response def view_two(request): raise ValueError(foo) def excview(request): request.response.body = bAn exception was raised request.response.status_int = 500 return request.response if __name__ == __main__: config = Configurator() config.add_route(one, /view_one) config.add_route(two, /view_two) config.add_view(view_one, route_name=one) config.add_view(view_two, route_name=two, renderer=string) config.add_view(excview, context=Exception) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
When we run the above code and visit /view_one in a browser, the excview exception view will not be executed. Instead, the call to invoke_subrequest() will cause a ValueError exception to be raised and a response will never be generated. We can change this behavior; how to do so is described below in our discussion of the use_tweens argument. The pyramid.request.Request.invoke_subrequest() API accepts two arguments: a positional argument request that must be provided, and use_tweens keyword argument that is optional; it defaults to False. The request object passed to the API must be an object that implements the Pyramid request interface (such as a pyramid.request.Request instance). If use_tweens is True, the request will be 317
29. INVOKING A SUBREQUEST sent to the tween in the tween stack closest to the request ingress. If use_tweens is False, the request will be sent to the main router handler, and no tweens will be invoked. In the example above, the call to invoke_subrequest() will always raise an exception. This is because its using the default value for use_tweens, which is False. You can pass use_tweens=True instead to ensure that it will convert an exception to a Response if an exception view is congured instead of raising the exception. This because exception views are called by the exception view tween as described in Custom Exception Views when any view raises an exception. We can cause the subrequest to be run through the tween stack by passing use_tweens=True to the call to invoke_subrequest(), like this:
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.request import Request def view_one(request): subreq = Request.blank(/view_two) response = request.invoke_subrequest(subreq, use_tweens=True) return response def view_two(request): raise ValueError(foo) def excview(request): request.response.body = bAn exception was raised request.response.status_int = 500 return request.response if __name__ == __main__: config = Configurator() config.add_route(one, /view_one) config.add_route(two, /view_two) config.add_view(view_one, route_name=one) config.add_view(view_two, route_name=two, renderer=string) config.add_view(excview, context=Exception) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
In the above case, the call to request.invoke_subrequest(subreq) will not raise an exception. Instead, it will retrieve a 500 response from the attempted invocation of view_two, because the tween which invokes an exception view to generate a response is run, and therefore excview is executed. This is one of the major differences between specifying the use_tweens=True and use_tweens=False arguments to invoke_subrequest(). use_tweens=True may 318
also imply invoking transaction commit/abort for the logic executed in the subrequest if youve got pyramid_tm in the tween list, injecting debug HTML if youve got pyramid_debugtoolbar in the tween list, and other tween-related side effects as dened by your particular tween list. The invoke_subrequest() function also unconditionally: manages the threadlocal stack so that get_current_request() and get_current_registry() work during a request (they will return the subrequest instead of the original request) Adds a registry attribute and a invoke_subrequest attribute (a callable) to the request object its handed. sets request extensions (such as those added via add_request_method() set_request_property()) on the subrequest object passed as request causes a NewRequest event to be sent at the beginning of request processing. causes a ContextFound event to be sent when a context resource is found. Ensures that the user implied by the request passed has the necessary authorization to invoke view callable before calling it. causes a NewResponse event to be sent when the Pyramid application returns a response. Calls any response callback functions dened within the subrequests lifetime if a response is obtained from the Pyramid application. Calls any nished callback functions dened within the subrequests lifetime. The invocation of a subrequest has more or less exactly the same effect as the invocation of a request received by the Pyramid router from a web client when use_tweens=True. When use_tweens=False, the tweens are skipped but all the other steps take place. Its a poor idea to use the original request object as an argument to invoke_subrequest(). You should construct a new request instead as demonstrated in the above example, using pyramid.request.Request.blank(). Once youve constructed a request object, youll need to massage the it to match the view callable youd like to be executed during the subrequest. This can be done by adjusting the subrequests URL, its headers, its request method, and other attributes. The documentation for pyramid.request.Request exposes the methods you should call and attributes you should set on the request you create to massage it into something that will actually match the view youd like to call via a subrequest. Weve demonstrated use of a subrequest from within a view callable, but you can use the invoke_subrequest() API from within a tween or an event handler as well. Its usually a poor idea to invoke invoke_subrequest() from within a tween, because tweens already by denition have access to a function that will cause a subrequest (they are passed a handle function), but you can do it. Its ne to invoke invoke_subrequest() from within an event handler, however. 319 or
320
CHAPTER
THIRTY
USING HOOKS
Hooks can be used to inuence the behavior of the Pyramid framework in various ways.
Replace helloworld.views.notfound with a reference to the view callable you want to use to represent the Not Found view. The not found view callable is a view callable like any other. If your application instead uses pyramid.view.view_config decorators and a scan, you can replace the Not Found view by using the pyramid.view.notfound_view_config decorator:
321
1 2 3 4 5 6 7 8 9
from pyramid.view import notfound_view_config @notfound_view_config() def notfound(request): return Response(Not Found, dude, status=404 Not Found) def main(globals, **settings): config = Configurator() config.scan()
This does exactly what the imperative example above showed. Your application can dene multiple not found views if necessary. Both pyramid.config.Configurator.add_notfound_view() and pyramid.view.notfound_view_config take most of the same arguments as pyramid.config.Configurator.add_view and pyramid.view.view_config, respectively. This means that not found views can carry predicates limiting their applicability. For example:
1 2 3 4 5 6 7 8 9 10 11 12 13
from pyramid.view import notfound_view_config @notfound_view_config(request_method=GET) def notfound_get(request): return Response(Not Found during GET, dude, status=404 Not Found) @notfound_view_config(request_method=POST) def notfound_post(request): return Response(Not Found during POST, dude, status=404 Not Found) def main(globals, **settings): config = Configurator() config.scan()
The notfound_get view will be called when a view could not be found and the request method was GET. The notfound_post view will be called when a view could not be found and the request method was POST. Like any other view, the notfound view must accept at least a request parameter, or both context and request. The request is the current request representing the denied action. The context (if used in the call signature) will be the instance of the HTTPNotFound exception that caused the view to be called. Both pyramid.config.Configurator.add_notfound_view() and pyramid.view.notfound_view_config can be used to automatically redirect requests to slash-appended routes. See Redirecting to Slash-Appended Routes for examples. 322
30.2. CHANGING THE FORBIDDEN VIEW Heres some sample code that implements a minimal NotFound view callable:
1 2 3 4
When a NotFound view callable is invoked, it is passed a request. The exception attribute of the request will be an instance of the HTTPNotFound exception that caused the not found view to be called. The value of request.exception.message will be a value explaining why the not found error was raised. This message will be different when the pyramid.debug_notfound environment setting is true than it is when it is false.
Both pyramid.config.Configurator.add_notfound_view() and pyramid.view.notfound_view_config are new as of Pyramid 1.3. Older Pyramid documentation instructed users to use add_view instead, with a context of HTTPNotFound. This still works; the convenience method and decorator are just wrappers around this functionality.
When a NotFound view callable accepts an argument list as described in Alternate View Callable Argument/Calling Conventions, the context passed as the rst argument to the view callable will be the HTTPNotFound exception instance. If available, the resource context will still be available as request.context.
For example, you can add a forbidden view by using the pyramid.config.Configurator.add_forbidden_view() method to register a forbidden view: 323
1 2 3
Replace helloworld.views.forbidden_view with a reference to the Python view callable you want to use to represent the Forbidden view. If instead you prefer to use decorators and a scan, you can use the pyramid.view.forbidden_view_config decorator to mark a view callable as a forbidden view:
1 2 3 4 5 6 7 8 9
from pyramid.view import forbidden_view_config @forbidden_view_config() def forbidden(request): return Response(forbidden) def main(globals, **settings): config = Configurator() config.scan()
Like any other view, the forbidden view must accept at least a request parameter, or both context and request. If a forbidden view callable accepts both context and request, the HTTP Exception is passed as context. The context as found by the router when view was denied (that you normally would expect) is available as request.context. The request is the current request representing the denied action. Heres some sample code that implements a minimal forbidden view:
1 2 3 4 5
from pyramid.view import view_config from pyramid.response import Response def forbidden_view(request): return Response(forbidden)
When a forbidden view callable is invoked, it is passed a request. The exception attribute of the request will be an instance of the HTTPForbidden exception that caused the forbidden view to be called. The value of request.exception.message will be a value explaining why the forbidden was raised and request.exception.result will be extended information about the forbidden exception. These messages will be different when the pyramid.debug_authorization environment setting is true than it is when it is false.
324
If youre doing imperative conguration, and youd rather do youve already constructed a congurator it can also be registered pyramid.config.Configurator.set_request_factory() method:
1 2 3 4 5 6 7 8
from pyramid.config import Configurator from pyramid.request import Request class MyRequest(Request): pass config = Configurator() config.set_request_factory(MyRequest)
325
1 2 3 4 5 6
from pyramid.events import subscriber from pyramid.events import BeforeRender @subscriber(BeforeRender) def add_global(event): event[mykey] = foo
An object of this type is sent as an event just before a renderer is invoked (but after the application-level renderer globals factory added via set_renderer_globals_factory, if any, has injected its own keys into the renderer globals dictionary). If a subscriber attempts to add a key that already exist in the renderer globals dictionary, a KeyError is raised. This limitation is enforced because event subscribers do not possess any relative ordering. The set of keys added to the renderer globals dictionary by all pyramid.events.BeforeRender subscribers and renderer globals factories must be unique. The dictionary returned from the view is accessible through the rendering_val attribute of a BeforeRender event. Suppose you return {mykey: view callable, like so:
1 2 3 4 5
somevalue, mykey2:
from pyramid.view import view_config @view_config(renderer=some_renderer) def myview(request): return {mykey: somevalue, mykey2: somevalue2}
rendering_val can be used to access these values from the BeforeRender object:
1 2 3 4 5 6 7
from pyramid.events import subscriber from pyramid.events import BeforeRender @subscriber(BeforeRender) def read_return(event): # {mykey: somevalue} is returned from the view print(event.rendering_val[mykey])
BeforeRender
event
interface
at
Another (deprecated) mechanism which allows event subscribers more control when adding renderer global values exists in Adding Renderer Globals (Deprecated). 326
this feature is deprecated as of Pyramid 1.1. A non-deprecated mechanism which allows event subscribers to add renderer global values is documented in Using The Before Render Event.
Whenever Pyramid handles a request to perform a rendering (after a view with a renderer= conguration attribute is invoked, or when any of the methods beginning with render within the pyramid.renderers module are called), renderer globals can be injected into the system values sent to the renderer. By default, no renderer globals are injected, and the bare system values (such as request, context, view, and renderer_name) are the only values present in the system dictionary passed to every renderer. A callback that Pyramid will call every time a renderer is invoked can be added by passing a renderer_globals_factory argument to the constructor of the congurator. This callback can either be a callable object or a dotted Python name representing such a callable.
1 2 3 4 5
Such a callback must accept a single positional argument (notionally named system) which will contain the original system values. It must return a dictionary of values that will be merged into the system dictionary. See System Values Used During Rendering for description of the values present in the system dictionary. If youre doing imperative conguration, and youd rather do it after youve already constructed a congurator it can also be registered via the pyramid.config.Configurator.set_renderer_globals_factory() method:
1 2 3 4 5 6 7
from pyramid.config import Configurator def renderer_globals_factory(system): return {a: 1} config = Configurator() config.set_renderer_globals_factory(renderer_globals_factory)
327
def cache_callback(request, response): """Set the cache_control max_age for the response""" if request.exception is not None: response.cache_control.max_age = 360 request.add_response_callback(cache_callback)
No response callback is called if an unhandled exception happens in application code, or if the response object returned by a view callable is invalid. Response callbacks are, however, invoked when a exception view is rendered successfully: in such a case, the request.exception attribute of the request when it enters a response callback will be an exception object instead of its default value of None. Response callbacks are called in the order theyre added (rst-to-most-recently-added). All response callbacks are called after the NewResponse event is sent. Errors raised by response callbacks are not handled specially. They will be propagated to the caller of the Pyramid router application. A response callback has a lifetime of a single request. If you want a response callback to happen as the result of every request, you must re-register the callback into every new request (perhaps within a subscriber of a NewRequest event).
1 2 3 4 5 6 7 8
import logging log = logging.getLogger(__name__) def log_callback(request): """Log information at the end of request""" log.debug(Request is finished.) request.add_finished_callback(log_callback)
Finished callbacks are called in the order theyre added (rst-to-most-recently-added). Finished callbacks (unlike a response callback) are always called, even if an exception happens in application code that prevents a response from being generated. The set of nished callbacks associated with a request are called very late in the processing of that request; they are essentially the very last thing called by the router before a request ends. They are called after response processing has already occurred in a top-level finally: block within the router request processing code. As a result, mutations performed to the request provided to a nished callback will have no meaningful effect, because response processing will have already occurred, and the requests scope will expire almost immediately after all nished callbacks have been processed. Errors raised by nished callbacks are not handled specially. They will be propagated to the caller of the Pyramid router application. A nished callback has a lifetime of a single request. If you want a nished callback to happen as the result of every request, you must re-register the callback into every new request (perhaps within a subscriber of a NewRequest event).
from pyramid.config import Configurator from myapp.traversal import Traverser config = Configurator() config.add_traverser(Traverser)
In the example above, myapp.traversal.Traverser is assumed to be a class that implements the following interface:
329
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
class Traverser(object): def __init__(self, root): """ Accept the root object returned from the root factory """ def __call__(self, request): """ Return a dictionary with (at least) the keys root, context, view_name, subpath, traversed, virtual_root, and virtual_root_path. These values are typically the result of a resource tree traversal. root is the physical root object, context will be a resource object, view_name will be the view name used (a Unicode name), subpath will be a sequence of Unicode names that followed the view name but were not traversed, traversed will be a sequence of Unicode names that were traversed (including the virtual root path, if any) virtual_root will be a resource object representing the virtual root (or the physical root if traversal was not performed), and virtual_root_path will be a sequence representing the virtual root path (a sequence of Unicode names) or None if traversal was not performed. Extra keys for special purpose functionality can be added as necessary. All values returned in the dictionary will be made available as attributes of the request object. """
More than one traversal algorithm can be active at the same time. For instance, if your root factory returns more than one type of object conditionally, you could claim that an alternate traverser adapter is for only one particular class or interface. When the root factory returned an object that implemented that class or interface, a custom traverser would be used. Otherwise, the default traverser would be used. For example:
1 2 3 4 5
from myapp.traversal import Traverser from myapp.resources import MyRoot from pyramid.config import Configurator config = Configurator() config.add_traverser(Traverser, MyRoot)
If the above stanza was added to a Pyramid __init__.py les main function, Pyramid would use the myapp.traversal.Traverser only when the application root factory returned an instance of the myapp.resources.MyRoot object. Otherwise it would use the default Pyramid traverser to do traversal.
330
from myapp.traversal import ResourceURLAdapter from myapp.resources import MyRoot config.add_resource_url_adapter(ResourceURLAdapter, MyRoot)
In the above example, the myapp.traversal.ResourceURLAdapter class will be used to provide services to resource_url() any time the resource passed to resource_url is of the class myapp.resources.MyRoot. The resource_iface argument MyRoot represents the type of interface that must be possessed by the resource for this resource url factory to be found. If the resource_iface argument is omitted, this resource url adapter will be used for all resources. The API that must be implemented by your a class that provides IResourceURL is as follows:
1 2 3 4 5 6 7 8 9
class MyResourceURL(object): """ An adapter which provides the virtual and physical paths of a resource """ def __init__(self, resource, request): """ Accept the resource and request and set self.physical_path and self.virtual_path""" self.virtual_path = some_function_of(resource, request) self.physical_path = some_other_function_of(resource, request)
The default context URL generator is available for perusal as the class pyramid.traversal.ResourceURL in the traversal module of the Pylons GitHub Pyramid repository. See pyramid.config.add_resource_url_adapter() for more information. 331
Pyramid, in various places, adapts the result of calling a view callable to the IResponse interface to ensure that the object returned by the view callable is a true response object. The vast majority of time, the result of this adaptation is the result object itself, as view callables written by civilians who read the narrative documentation contained in this manual will always return something that implements the IResponse interface. Most typically, this will be an instance of the pyramid.response.Response class or a subclass. If a civilian returns a non-Response object from a view callable that isnt congured to use a renderer, he will typically expect the router to raise an error. However, you can hook Pyramid in such a way that users can return arbitrary values from a view callable by providing an adapter which converts the arbitrary return value into something that implements IResponse. For example, if youd like to allow view callables to return bare string objects (without requiring a renderer to convert a string to a response object), you can register an adapter which converts the string to a Response:
1 2 3 4 5 6 7 8 9
from pyramid.response import Response def string_response_adapter(s): response = Response(s) return response # config is an instance of pyramid.config.Configurator config.add_response_adapter(string_response_adapter, str)
Likewise, if you want to be able to return a simplied kind of response object from view callables, you can use the IResponse hook to register an adapter to the more complex IResponse interface:
1 2 3 4
332
5 6 7 8 9 10 11 12 13
self.body = body def simple_response_adapter(simple_response): response = Response(simple_response.body) return response # config is an instance of pyramid.config.Configurator config.add_response_adapter(simple_response_adapter, SimpleResponse)
If you want to implement your own Response object instead of using the pyramid.response.Response object in any capacity at all, youll have to make sure the object implements every attribute and method outlined in pyramid.interfaces.IResponse and youll have to ensure that it uses zope.interface.implementer(IResponse) as a class decoratoror.
1 2 3 4 5 6 7
from pyramid.interfaces import IResponse from zope.interface import implementer @implementer(IResponse) class MyResponse(object): # ... an implementation of every method and attribute # documented in IResponse should follow ...
When an alternate response object implementation is returned by a view callable, if that object asserts that it implements IResponse (via zope.interface.implementer(IResponse)) , an adapter neednt be registered for the object; Pyramid will use it directly. An IResponse adapter for webob.Response (as opposed to pyramid.response.Response) is registered by Pyramid by default at startup time, as by their nature, instances of this class (and instances of subclasses of the class) will natively provide IResponse. The adapter registered for webob.Response simply returns the response object. Instead of using pyramid.config.Configurator.add_response_adapter(), you can use the pyramid.response.response_adapter decorator:
1 2 3 4 5 6 7
from pyramid.response import Response from pyramid.response import response_adapter @response_adapter(str) def string_response_adapter(s): response = Response(s) return response
333
30. USING HOOKS The above example, when scanned, has the same effect as:
config.add_response_adapter(string_response_adapter, str)
# framework class PylonsControllerViewMapper(object): def __init__(self, **kw): self.kw = kw def __call__(self, view):
334
8 9 10 11 12 13 14 15 16 17 18
attr = self.kw[attr] def wrapper(context, request): matchdict = request.matchdict.copy() matchdict.pop(action, None) inst = view(request) meth = getattr(inst, attr) return meth(**matchdict) return wrapper class BaseController(object): __view_mapper__ = PylonsControllerViewMapper
# user application from pyramid.response import Response from pyramid.config import Configurator import pyramid_handlers from wsgiref.simple_server import make_server class MyController(BaseController): def index(self, id): return Response(id) if __name__ == __main__: config = Configurator() config.include(pyramid_handlers) config.add_handler(one, /{id}, MyController, action=index) config.add_handler(two, /{action}/{id}, MyController) server.make_server(0.0.0.0, 8080, config.make_wsgi_app()) server.serve_forever()
The pyramid.config.Configurator.set_view_mapper() method can be used to set a default view mapper (overriding the superdefault view mapper used by Pyramid itself). A single view registration can use a view mapper by passing the mapper as the mapper argument to add_view().
30. USING HOOKS You may wish to have your own decorators that offer such behaviour. This is possible by using the Venusian package in the same way that it is used by Pyramid. By way of example, lets suppose you want to write a decorator that registers the function it wraps with a Zope Component Architecture utility within the application registry provided by Pyramid. The application registry and the utility inside the registry is likely only to be available once your applications conguration is at least partially completed. A normal decorator would fail as it would be executed before the conguration had even begun. However, using Venusian, the decorator could be written as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import venusian from mypackage.interfaces import IMyUtility class registerFunction(object): def __init__(self, path): self.path = path def register(self, scanner, name, wrapped): registry = scanner.config.registry registry.getUtility(IMyUtility).register( self.path, wrapped) def __call__(self, wrapped): venusian.attach(wrapped, self.register) return wrapped
This decorator could then be used to register functions throughout your code:
1 2 3
However, the utility would only be looked up when a scan was performed, enabling you to set up the utility in advance:
1 2 3 4 5 6 7
from zope.interface import implementer from wsgiref.simple_server import make_server from pyramid.config import Configurator from mypackage.interfaces import IMyUtility @implementer(IMyUtility)
336
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
class UtilityImplementation: def __init__(self): self.registrations = {} def register(self, path, callable_): self.registrations[path] = callable_ if __name__ == __main__: config = Configurator() config.registry.registerUtility(UtilityImplementation()) config.scan() app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
Tweens are a feature which were added in Pyramid 1.2. They are not available in any previous version. A tween (a contraction of the word between) is a bit of code that sits between the Pyramid routers main request handling function and the upstream WSGI component that uses Pyramid as its app. This is a feature that may be used by Pyramid framework extensions, to provide, for example, Pyramid-specic view timing support bookkeeping code that examines exceptions before they are returned to the upstream WSGI application. Tweens behave a bit like WSGI middleware but they have the benet of running in a context in which they have access to the Pyramid application registry as well as the Pyramid rendering machinery.
337
30. USING HOOKS application registry represented by this Congurator. A tween factory must return a tween when it is called. A tween is a callable which accepts a request object and returns a response object. Heres an example of a tween factory:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
# in a module named myapp.tweens import time from pyramid.settings import asbool import logging log = logging.getLogger(__name__) def timing_tween_factory(handler, registry): if asbool(registry.settings.get(do_timing)): # if timing support is enabled, return a wrapper def timing_tween(request): start = time.time() try: response = handler(request) finally: end = time.time() log.debug(The request took %s seconds % (end - start)) return response return timing_tween # if timing support is not enabled, return the original # handler return handler
If you remember, a tween is an object which accepts a request object and which returns a response argument. The request argument to a tween will be the request created by Pyramids router when it receives a WSGI request. The response object will be generated by the downstream Pyramid application and it should be returned by the tween. In the above example, the tween factory denes a timing_tween tween and returns it if asbool(registry.settings.get(do_timing)) is true. It otherwise simply returns the handler it was given. The registry.settings attribute is a handle to the deployment settings provided by the user (usually in an .ini le). In this case, if the user has dened a do_timing setting, and that setting is True, the user has said she wants to do timing, so the tween factory returns the timing tween; it otherwise just returns the handler it has been provided, preventing any timing. The example timing tween simply records the start time, calls the downstream handler, logs the number of seconds consumed by the downstream handler, and returns the response. 338
Note that you must use a dotted Python name as the rst argument to pyramid.config.Configurator.add_tween(); this must point at a tween factory. You cannot pass the tween factory object itself to the method: it must be dotted Python name that points to a globally importable object. In the above example, we assume that a timing_tween_factory tween factory was dened in a module named myapp.tweens, so the tween factory is importable as myapp.tweens.timing_tween_factory. When you use pyramid.config.Configurator.add_tween(), youre instructing the system to use your tween factory at startup time unless the user has provided an explicit tween list in his conguration. This is whats meant by an implicit tween. A user can always elect to supply an explicit tween list, reordering or disincluding implicitly added tweens. See Explicit Tween Ordering for more information about explicit tween ordering. If more than one call to pyramid.config.Configurator.add_tween() is made within a single application conguration, the tweens will be chained together at application startup time. The rst tween factory added via add_tween will be called with the Pyramid exception view tween factory as its handler argument, then the tween factory added directly after that one will be called with the result of the rst tween factory as its handler argument, and so on, ad innitum until all tween factories have been called. The Pyramid router will use the outermost tween produced by this chain (the tween generated by the very last tween factory added) as its request handler function. For example:
1 2 3 4 5
The above example will generate an implicit tween chain that looks like this:
339
An iterable of any combination of the above. This allows the user to specify fallbacks if the desired tween is not included, as well as compatibility with multiple other tweens. Effectively, under means closer to the main Pyramid application than, over means closer to the request ingress than. For example, the following call to add_tween() will attempt to place the tween factory represented by myapp.tween_factory directly above (in ptweens order) the main Pyramid request handler.
1 2 3
The above example will generate an implicit tween chain that looks like this:
340
Likewise, calling the following call to add_tween() will attempt to place this tween factory above the main handler but below a separately added tween factory:
1 2 3 4 5 6 7
The above example will generate an implicit tween chain that looks like this:
INGRESS (implicit) pyramid.tweens.excview_tween_factory (implicit) myapp.tween_factory1 myapp.tween_factory2 MAIN (implicit)
Specifying neither over nor under is equivalent to specifying under=INGRESS. If all options for under (or over) cannot be found in the current conguration, it is an error. If some options are specied purely for compatibilty with other tweens, just add a fallback of MAIN or INGRESS. For example, under=(someothertween, someothertween2, INGRESS). This constraint will require the tween to be located under both the someothertween tween, the someothertween2 tween, and INGRESS. If any of these is not in the current conguration, this constraint will only organize itself based on the tweens that are present.
1 2 3 4 5 6 7 8 9
[app:main] use = egg:MyApp pyramid.reload_templates = true pyramid.debug_authorization = false pyramid.debug_notfound = false pyramid.debug_routematch = false pyramid.debug_templates = true pyramid.tweens = myapp.my_cool_tween_factory pyramid.tweens.excview_tween_factory
In the above conguration, calls made during conguration to pyramid.config.Configurator.add_tween() are ignored, and the user is telling the system to use the tween factories he has listed in the pyramid.tweens conguration setting (each is a dotted Python name which points to a tween factory) instead of any tween factories added via pyramid.config.Configurator.add_tween(). The rst tween factory in the pyramid.tweens list will be used as the producer of the effective Pyramid request handling function; it will wrap the tween factory declared directly below it, ad innitum. The main Pyramid request handler is implicit, and always at the bottom.
Pyramids own exception view handling logic is implemented as a tween factory function: pyramid.tweens.excview_tween_factory(). If Pyramid exception view handling is desired, and tween factories are specied via the pyramid.tweens conguration setting, the pyramid.tweens.excview_tween_factory() function must be added to the pyramid.tweens conguration setting list explicitly. If it is not present, Pyramid will not perform exception view handling.
342
Third-party view, route, and subscriber predicates are a feature new as of Pyramid 1.4.
Many other built-in predicates exists (request_param, and others). You can add third-party predicates to the list of available predicates by using one of pyramid.config.Configurator.add_view_predicate() or pyramid.config.Configurator.add_route_predicate(). The former adds a view predicate, the latter a route predicate. When using one of those APIs, you pass a name and a factory to add a predicate during Pyramids conguration stage. For example:
343
config.add_view_predicate(content_type, ContentTypePredicate)
The above example adds a new predicate named content_type to the list of available predicates for views. This will allow the following view conguration statement to work:
1 2
The rst argument to pyramid.config.Configurator.add_view_predicate(), the name, is a string representing the name that is expected to be passed to view_config (or its imperative analogue add_view). The second argument is a view or route predicate factory. A view or route predicate factory is most often a class with a constructor (__init__), a text method, a phash method and a __call__ method. For example:
1 2 3 4 5 6 7 8 9 10 11
class ContentTypePredicate(object): def __init__(self, val, config): self.val = val def text(self): return content_type = %s % (self.val,) phash = text def __call__(self, context, request): return getattr(context, content_type, None) == self.val
The constructor of a predicate factory takes two arguments: val and config. The val argument will be the argument passed to view_config (or add_view). In the example above, it will be the string File. The second arg, config will be the Congurator instance at the time of conguration. The text method must return a string. It should be useful to describe the behavior of the predicate in error messages. The phash method must return a string or a sequence of strings. Its most often the same as text, as long as text uniquely describes the predicates name and the value passed to the constructor. If text is more general, or doesnt describe things that way, phash should return a string with the name and the value serialized. The result of phash is not seen in output anywhere, it just informs the uniqueness constraints for view conguration. The __call__ method of a predicate factory must accept a resource (context) and a request, and must return True or False. It is the meat of the predicate. You can use the same predicate factory as both a view predicate and as a route predicate, but youll need to call add_view_predicate and add_route_predicate separately with the same factory. 344
class RequestPathStartsWith(object): def __init__(self, val, config): self.val = val def text(self): return path_startswith = %s % (self.val,) phash = text def __call__(self, event): return event.request.path.startswith(self.val)
Once youve created a subscriber predicate, it may registered pyramid.config.Configurator.add_subscriber_predicate(). For example:
config.add_subscriber_predicate( request_path_startswith, RequestPathStartsWith)
via
Once a subscriber predicate is registered, you can use it in a call to pyramid.config.Configurator.add_subscriber() or to pyramid.events.subscriber. Heres an example of using the previously registered request_path_startswith predicate in a call to add_subscriber():
345
1 2 3 4 5 6 7 8 9
# define a subscriber in your code def yosubscriber(event): event.request.yo = YO! # and at configuration time config.add_subscriber(yosubscriber, NewRequest, request_path_startswith=/add_yo)
from pyramid.events import subscriber @subscriber(NewRequest, request_path_startswith=/add_yo) def yosubscriber(event): event.request.yo = YO!
In either of the above congurations, the yosubscriber callable will only be called if the request path starts with /add_yo. Otherwise the event subscriber will not be called. Note that the request_path_startswith subscriber you dened can be used with events that have a request attribute, but not ones that do not. So, for example, the predicate can be used with subscribers registered for pyramid.events.NewRequest and pyramid.events.ContextFound events, but it cannot be used with subscribers registered for pyramid.events.ApplicationCreated because the latter type of event has no request attribute. The point being: unlike route and view predicates, not every type of subscriber predicate will necessarily be applicable for use in every subscriber registration. It is not the responsibility of the predicate author to make every predicate make sense for every event type; it is the responsibility of the predicate consumer to use predicates that make sense for a particular event type registration.
346
CHAPTER
THIRTYONE
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]))
347
31. PYRAMID CONFIGURATION INTROSPECTION This view will return a response that contains the pattern argument provided to the add_route method of the route which matched when the view was called. It uses the pyramid.interfaces.IIntrospector.get() method to return an introspectable in the category routes with a discriminator equal to the matched route name. It then uses the returned introspectable to obtain a pattern value. The introspectable returned by the query methods of the introspector has methods and attributes described by pyramid.interfaces.IIntrospectable. In particular, the get(), get_category(), categories(), categorized(), and related() methods of an introspector can be used to query for introspectables.
31.3. PYRAMID INTROSPECTION CATEGORIES action_info An object describing the directive call site which caused this introspectable to be registered; contains attributes described in pyramid.interfaces.IActionInfo. Besides having the attributes described above, an introspectable is a dictionary-like object. An introspectable can be queried for data values via its __getitem__, get, keys, values, or items methods. For example:
1 2
31. PYRAMID CONFIGURATION INTROSPECTION The predicate objects created as the result of passing predicate arguments to add_susbcriber derived_predicates Wrappers around the predicate objects created as the result of passing predicate arguments to add_susbcriber (to be used when predicates take only one value but must be passed more than one). response adapters Each introspectable in the response adapters category represents a call to pyramid.config.Configurator.add_response_adapter() (or a decorator equivalent); each will have the following data. adapter The adapter object (the add_response_adapter). type The resolved type_or_iface add_response_adapter. root factories Each introspectable in the root factories category represents a call to pyramid.config.Configurator.set_root_factory() (or the Congurator constructor equivalent) or a factory argument passed to pyramid.config.Configurator.add_route(); each will have the following data. factory The factory object (the resolved factory argument to set_root_factory). route_name The name of the route which will use this factory. If this is the default root factory (if its registered during a call to set_root_factory), this value will be None. session factory 350 argument passed to resolved adapter argument to
31.3. PYRAMID INTROSPECTION CATEGORIES Only one introspectable will exist in the session factory category. It represents a call to pyramid.config.Configurator.set_session_factory() (or the Congurator constructor equivalent); it will have the following data. factory The factory object (the set_session_factory). request factory Only one introspectable will exist in the request factory category. It represents a call to pyramid.config.Configurator.set_request_factory() (or the Congurator constructor equivalent); it will have the following data. factory The factory object (the set_request_factory). locale negotiator Only one introspectable will exist in the locale negotiator category. It represents a call to pyramid.config.Configurator.set_locale_negotiator() (or the Congurator constructor equivalent); it will have the following data. negotiator The factory object (the set_locale_negotiator). renderer factories Each introspectable in the renderer factories category represents a call to pyramid.config.Configurator.add_renderer() (or the Congurator constructor equivalent); each will have the following data. name The name of the renderer (the value of the name argument to add_renderer). factory The factory object (the resolved factory argument to add_renderer). 351 resolved negotiator argument to resolved factory argument to resolved factory argument to
31. PYRAMID CONFIGURATION INTROSPECTION renderer globals factory There will be one and only one introspectable in the renderer globals factory category. It represents a call to pyramid.config.Configurator.set_renderer_globals_factory(); it will have the following data. factory The factory object (the resolved set_renderer_globals_factory). routes Each introspectable in the routes category represents a call to pyramid.config.Configurator.add_route(); each will have the following data. name The name argument passed to add_route. pattern The pattern argument passed to add_route. factory The (resolved) factory argument passed to add_route. xhr The xhr argument passed to add_route. request_method The request_method argument passed to add_route. request_methods A sequence of request method names implied by the request_method argument passed to add_route or the value None if a request_method argument was not supplied. 352 factory argument to
31.3. PYRAMID INTROSPECTION CATEGORIES path_info The path_info argument passed to add_route. request_param The request_param argument passed to add_route. header The header argument passed to add_route. accept The accept argument passed to add_route. traverse The traverse argument passed to add_route. custom_predicates The custom_predicates argument passed to add_route. pregenerator The pregenerator argument passed to add_route. static The static argument passed to add_route. use_global_views The use_global_views argument passed to add_route. object The pyramid.interfaces.IRoute object that is used to perform matching and generation for this route. authentication policy 353
31. PYRAMID CONFIGURATION INTROSPECTION There will be one and only one introspectable in the authentication policy category. It represents a call to the pyramid.config.Configurator.set_authentication_policy() method (or its Congurator constructor equivalent); it will have the following data. policy The policy object (the resolved set_authentication_policy). authorization policy There will be one and only one introspectable in the authorization policy category. It represents a call to the pyramid.config.Configurator.set_authorization_policy() method (or its Congurator constructor equivalent); it will have the following data. policy The policy object (the resolved set_authorization_policy). default permission There will be one and only one introspectable default permission category. It represents a call pyramid.config.Configurator.set_default_permission() (or its Congurator constructor equivalent); it will have the following data. value The permission name passed to set_default_permission. views Each introspectable in the views category represents a call to pyramid.config.Configurator.add_view(); each will have the following data. name The name argument passed to add_view. context 354 in the to the method policy argument to policy argument to
31.3. PYRAMID INTROSPECTION CATEGORIES The (resolved) context argument passed to add_view. containment The (resolved) containment argument passed to add_view. request_param The request_param argument passed to add_view. request_methods A sequence of request method names implied by the request_method argument passed to add_view or the value None if a request_method argument was not supplied. route_name The route_name argument passed to add_view. attr The attr argument passed to add_view. xhr The xhr argument passed to add_view. accept The accept argument passed to add_view. header The header argument passed to add_view. path_info The path_info argument passed to add_view. match_param The match_param argument passed to add_view. 355
31. PYRAMID CONFIGURATION INTROSPECTION csrf_token The csrf_token argument passed to add_view. callable The (resolved) view argument passed to add_view. Represents the raw view callable. derived_callable The view callable derived from the view argument passed to add_view. Represents the view callable which Pyramid itself calls (wrapped in security and other wrappers). mapper The (resolved) mapper argument passed to add_view. decorator The (resolved) decorator argument passed to add_view. permissions Each introspectable in the permissions category represents a call to pyramid.config.Configurator.add_view() that has an explicit permission argument to or a call to pyramid.config.Configurator.set_default_permission(); each will have the following data. value The permission name passed to add_view or set_default_permission. templates Each introspectable in the templates category represents a call to pyramid.config.Configurator.add_view() that has a renderer argument which points to a template; each will have the following data. name The renderers name (a string). 356
31.3. PYRAMID INTROSPECTION CATEGORIES type The renderers type (a string). renderer The pyramid.interfaces.IRendererInfo object which represents this templates renderer. view mapper Each introspectable in the permissions category represents a call to pyramid.config.Configurator.add_view() that has an explicit mapper argument to or a call to pyramid.config.Configurator.set_view_mapper(); each will have the following data. mapper The (resolved) mapper set_view_mapper. asset overrides Each introspectable in the asset overrides category represents a call to pyramid.config.Configurator.override_asset(); each will have the following data. to_override The to_override argument (an asset spec) passed to override_asset. override_with The override_with argument (an asset spec) passed to override_asset. translation directories Each introspectable in the asset overrides category represents an individual element in a specs argument passed to pyramid.config.Configurator.add_translation_dirs(); each will have the following data. directory 357 argument passed to add_view or
31. PYRAMID CONFIGURATION INTROSPECTION The absolute path of the translation directory. spec The asset specication passed to add_translation_dirs. tweens Each introspectable in the tweens category represents a call to pyramid.config.Configurator.add_tween(); each will have the following data. name The dotted name to the tween factory as a string (passed as the tween_factory argument to add_tween). factory The (resolved) tween factory object. type implict or explicit as a string. under The under argument passed to add_tween (a string). over The over argument passed to add_tween (a string). static views Each introspectable in the static views category represents a call to pyramid.config.Configurator.add_static_view(); each will have the following data. name The name argument provided to add_static_view. spec 358
31.3. PYRAMID INTROSPECTION CATEGORIES A normalized version of the spec argument provided to add_static_view. traversers Each introspectable in the traversers category represents a call to pyramid.config.Configurator.add_traverser(); each will have the following data. iface The (resolved) interface or class object that represents the return value of a root factory that this traverser will be used for. adapter The (resolved) traverser class. resource url adapters Each introspectable in the resource url adapters category represents a call to pyramid.config.Configurator.add_resource_url_adapter(); each will have the following data. adapter The (resolved) resource URL adapter class. resource_iface The (resolved) interface or class object that represents the resource interface that this url adapter is registered for. request_iface The (resolved) interface or class object that represents the request interface that this url adapter is registered for.
359
When introspection is False, all introspectables generated by conguration directives are thrown away.
360
CHAPTER
THIRTYTWO
32. EXTENDING AN EXISTING PYRAMID APPLICATION for such a pattern to work satisfactorily. Because Pyramid is not very opinionated, developers are able to use wildly different patterns and technologies to build an application. A given Pyramid application may happen to be reusable by a particular third party integrator, because the integrator and the original developer may share similar base technology choices (such as the use of a particular relational database or ORM). But the same application may not be reusable by a different developer, because he has made different technology choices which are incompatible with the original developers. As a result, the concept of a pluggable application is left to layers built above Pyramid, such as a CMS layer or application server layer. Such layers are apt to provide the necessary opinions (such as mandating a storage layer, a templating system, and a structured, well-documented pattern of registering that certain URLs map to certain bits of code) which makes the concept of a pluggable application possible. Pluggable applications, thus, should not plug in to Pyramid itself but should instead plug into a system written atop Pyramid. Although it does not provide for pluggable applications, Pyramid does provide a rich set of mechanisms which allows for the extension of a single existing application. Such features can be used by frameworks built using Pyramid as a base. All Pyramid applications may not be pluggable, but all Pyramid applications are extensible.
from pyramid.config import Configurator if __name__ == __main__: config = Configurator() config.add_view(myapp.views.view1, name=view1) config.add_view(myapp.views.view2, name=view2)
You should do move the calls to add_view outside of the (non-reusable) if __name__ == __main__ block, and into a reusable function:
1 2 3
362
4 5 6 7 8 9
Doing this allows an integrator to maximally reuse the conguration statements that relate to your application by allowing him to selectively include or disinclude the conguration functions youve created from an override package. Alternately, you can use ZCML for the purpose of making conguration extensible and overrideable. ZCML declarations that belong to an application can be overridden and extended by integrators as necessary in a similar fashion. If you use only ZCML to congure your application, it will automatically be maximally extensible without any manual effort. See pyramid_zcml for information about using ZCML.
1 2 3
If you want to override conguration in the application, you may need to run pyramid.config.Configurator.commit() after performing the scan of the original package, then add additional conguration that registers more views or routes which performs overrides.
1 2 3 4
Once this is done, you should be able to extend or override the application like any other (see Extending the Application). You can alternately just prevent a scan from happening (by omitting any call to the pyramid.config.Configurator.scan() method). This will cause the decorators attached to objects in the target application to do nothing. At this point, you will need to convert all the conguration done in decorators into equivalent imperative conguration or ZCML and add that conguration or ZCML to a separate Python package as described in Extending the Application.
32.3. EXTENDING AN EXISTING APPLICATION Change the main function in the new packages __init__.py to include the original Pyramid applications conguration functions via pyramid.config.Configurator.include() statements or a scan. Wire the new views and assets created in the new package up using imperative registrations within the main function of the __init__.py le of the new application. These wiring should happen after including the conguration functions of the old application. These registrations will extend or override any registrations performed by the original application. See Overriding Views, Overriding Routes and Overriding Assets.
You can override the rst view conguration statement made by configure_views within the override package, after loading the original conguration function:
1 2 3 4 5 6 7
from pyramid.config import Configurator from originalapp import configure_views if __name == __main__: config = Configurator() config.include(configure_views) config.add_view(theoverrideapp.views.theview, name=theview)
In this case, the theoriginalapp.views.theview view will never be executed. Instead, a new view, theoverrideapp.views.theview will be executed instead, when request circumstances dictate. A similar pattern can be used to extend the application with add_view declarations. Just register a new view against some other set of predicates to make sure the URLs it implies are available on some other page rendering. 365
366
CHAPTER
THIRTYTHREE
ADVANCED CONFIGURATION
To support application extensibility, the Pyramid Congurator, by default, detects conguration conicts and allows you to include conguration imperatively from other packages or modules. It also, by default, performs conguration in two separate phases. This allows you to ignore relative conguration statement ordering in some circumstances.
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response(Hello world!) if __name__ == __main__: config = Configurator() config.add_view(hello_world) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
When you start this application, all will be OK. However, what happens if we try to add another view to the conguration with the same set of predicate arguments as one weve already added?
367
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response(Hello world!) def goodbye_world(request): return Response(Goodbye world!) if __name__ == __main__: config = Configurator() config.add_view(hello_world, name=hello) # conflicting view configuration config.add_view(goodbye_world, name=hello) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
The application now has two conicting view conguration statements. When we try to start it again, it wont start. Instead, well receive a traceback that ends something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Traceback (most recent call last): File "app.py", line 12, in <module> app = config.make_wsgi_app() File "pyramid/config.py", line 839, in make_wsgi_app self.commit() File "pyramid/pyramid/config.py", line 473, in commit self._ctx.execute_actions() ... more code ... pyramid.exceptions.ConfigurationConflictError: Conflicting configuration actions For: (view, None, , None, <InterfaceClass pyramid.interfaces.IView>, None, None, None, None, None, False, None, None, None) Line 14 of file app.py in <module>: config.add_view(hello_world) Line 17 of file app.py in <module>: config.add_view(goodbye_world)
This traceback is trying to tell us: Weve got conicting information for a set of view conguration statements (The For: line).
368
33.1. CONFLICT DETECTION There are two statements which conict, shown beneath the For: config.add_view(hello_world. hello) on line 14 of app.py, config.add_view(goodbye_world, hello) on line 17 of app.py. line: and
These two conguration statements are in conict because weve tried to tell the system that the set of predicate values for both view congurations are exactly the same. Both the hello_world and goodbye_world views are congured to respond under the same set of circumstances. This circumstance: the view name (represented by the name= predicate) is hello. This presents an ambiguity that Pyramid cannot resolve. Rather than allowing the circumstance to go unreported, by default Pyramid raises a ConfigurationConflictError error and prevents the application from running. Conict detection happens for any kind of conguration: imperative conguration or conguration that results from the execution of a scan.
The Right Thing The most correct way to resolve conicts is to do the needful: change your conguration code to not have conicting conguration statements. The details of how this is done depends entirely on the conguration statements made by your application. Use the detail provided in the ConfigurationConflictError to track down the offending conicts and modify your conguration code accordingly. If youre getting a conict while trying to extend an existing application, and that application has a function which performs conguration like this one:
1 2
as
an
argument.
Instead,
use
369
config.include(add_routes)
Using include() instead of calling the function directly provides a modicum of automated conict resolution, with the conguration statements you dene in the calling code overriding those of the included function. See also Automatic Conict Resolution and Including Conguration from External Sources.
Using config.commit() You can manually commit a conguration by using the commit() method between conguration calls. For example, we prevent conicts from occurring in the application we examined previously as the result of adding a commit. Heres the application that generates conicts:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request): return Response(Hello world!) def goodbye_world(request): return Response(Goodbye world!) if __name__ == __main__: config = Configurator() config.add_view(hello_world, name=hello) # conflicting view configuration config.add_view(goodbye_world, name=hello) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
We can prevent the two add_view calls from conicting by issuing a call to commit() between them:
1 2 3 4 5
from wsgiref.simple_server import make_server from pyramid.config import Configurator from pyramid.response import Response def hello_world(request):
370
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
return Response(Hello world!) def goodbye_world(request): return Response(Goodbye world!) if __name__ == __main__: config = Configurator() config.add_view(hello_world, name=hello) config.commit() # commit any pending configuration actions # no-longer-conflicting view configuration config.add_view(goodbye_world, name=hello) app = config.make_wsgi_app() server = make_server(0.0.0.0, 8080, app) server.serve_forever()
In the above example weve issued a call to commit() between the two add_view calls. commit() will execute any pending conguration statements. Calling commit() is safe at any time. It executes all pending conguration actions and leaves the conguration action list clean. Note that commit() has no effect when youre using an autocommitting congurator (see Using An Autocommitting Congurator). Using An Autocommitting Congurator You can also use a heavy hammer to circumvent conict detection by using a congurator constructor parameter: autocommit=True. For example:
1 2 3 4
When the autocommit parameter passed to the Congurator is True, conict detection (and TwoPhase Conguration) is disabled. Conguration statements will be executed immediately, and succeeding statements will override preceding ones. commit() has no effect when autocommit is True. If you use a Congurator in code that performs unit testing, its usually a good idea to use an autocommitting Congurator, because you are usually unconcerned about conict detection or two-phase conguration in test code. 371
Rather than calling this function directly with config as an argument. pyramid.config.Configuration.include():
Instead, use
372
config.include(add_routes)
Using include rather than calling the function directly will allow Automatic Conict Resolution to work. include() can also accept a module as an argument:
1 2 3
For this to work properly, the myapp module must contain a callable with the special name includeme, which should perform conguration (like the add_routes callable we showed above as an example). include() can also accept a dotted Python name to a function or a module.
373
33. ADVANCED CONFIGURATION Even though the view statement depends on the registration of a custom renderer, due to two-phase conguration, the order in which the conguration statements are issued is not important. add_view will be able to nd the .rn renderer even if add_renderer is called after add_view. The same is untrue when you use an autocommitting congurator (see Using An Autocommitting Congurator). When an autocommitting congurator is used, two-phase conguration is disabled, and conguration statements must be ordered in dependency order. Some conguration methods, such as add_route() have internal ordering constraints: the routes they imply require relative ordering. Such ordering constraints are not absolved by two-phase conguration. Routes are still added in conguration execution order.
374
CHAPTER
THIRTYFOUR
from pyramid.events import NewRequest from pyramid.config import Configurator def add_newrequest_subscriber(config, subscriber): config.add_subscriber(subscriber, NewRequest) if __name__ == __main__: config = Configurator() config.add_directive(add_newrequest_subscriber, add_newrequest_subscriber)
375
34. EXTENDING PYRAMID CONFIGURATION Once add_directive() is called, a user can then call the added directive by its given name as if it were a built-in method of the Congurator:
1 2 3 4
A call to add_directive() is often hidden within an includeme function within a frameworky package meant to be included as per Including Conguration from External Sources via include(). For example, if you put this code in a package named pyramid_subscriberhelpers:
1 2 3
The user of the add-on package pyramid_subscriberhelpers would then be able to install it and subsequently do:
1 2 3 4 5 6 7
def mysubscriber(event): print event.request from pyramid.config import Configurator config = Configurator() config.include(pyramid_subscriberhelpers) config.add_newrequest_subscriber(mysubscriber)
376
1 2 3 4 5 6 7 8
def add_jammyjam(config, jammyjam): def register(): config.registry.jammyjam = jammyjam config.action(jammyjam, register) if __name__ == __main__: config = Configurator() config.add_directive(add_jammyjam, add_jammyjam)
Fancy, but what does it do? The action method accepts a number of arguments. In the above directive named add_jammyjam, we call action() with two arguments: the string jammyjam is passed as the rst argument named discriminator, and the closure function named register is passed as the second argument named callable. When the action() method is called, it appends an action to the list of pending conguration actions. All pending actions with the same discriminator value are potentially in conict with one another (see Conict Detection). When the commit() method of the Congurator is called (either explicitly or as the result of calling make_wsgi_app()), conicting actions are potentially automatically resolved as per Automatic Conict Resolution. If a conict cannot be automatically resolved, a ConfigurationConflictError is raised and application startup is prevented. In our above example, therefore, if a consumer of our add_jammyjam directive did this:
config.add_jammyjam(first) config.add_jammyjam(second)
When the action list was committed resulting from the set of calls above, our users application would not start, because the discriminators of the actions generated by the two calls are in direct conict. Automatic conict resolution cannot resolve the conict (because no config.include is involved), and the user provided no intermediate pyramid.config.Configurator.commit() call between the calls to add_jammyjam to ensure that the successive calls did not conict with each other. This demonstrates the purpose of the discriminator argument to the action method: its used to indicate a uniqueness constraint for an action. Two actions with the same discriminator will conict unless the conict is automatically or manually resolved. A discriminator can be any hashable object, but it is generally a string or a tuple. You use a discriminator to declaratively ensure that the user doesnt provide ambiguous conguration statements. But lets imagine that a consumer of add_jammyjam used it in such a way that no conguration conicts are generated.
377
config.add_jammyjam(first)
What happens now? When the add_jammyjam method is called, an action is appended to the pending actions list. When the pending conguration actions are processed during commit(), and no conicts occur, the callable provided as the second argument to the action() method within add_jammyjam is called with no arguments. The callable in add_jammyjam is the register closure function. It simply sets the value config.registry.jammyjam to whatever the user passed in as the jammyjam argument to the add_jammyjam function. Therefore, the result of the users call to our directive will set the jammyjam attribute of the registry to the string first. A callable is used by a directive to defer the result of a users call to the directive until conict detection has had a chance to do its job. Other arguments exist to the action() method, introspectables. including args, kw, order, and
args and kw exist as values, which, if passed, will be used as arguments to the callable function when it is called back. For example our directive might use them like so:
1 2 3 4 5 6
def add_jammyjam(config, jammyjam): def register(*arg, **kw): config.registry.jammyjam_args = arg config.registry.jammyjam_kw = kw config.registry.jammyjam = jammyjam config.action(jammyjam, register, args=(one,), kw={two:two})
In the above example, when this directive is used to generate an action, and that action is committed, config.registry.jammyjam_args will be set to (one,) and config.registry.jammyjam_kw will be set to {two:two}. args and kw are honestly not very useful when your callable is a closure function, because you already usually have access to every local in the directive without needing them to be passed back. They can be useful, however, if you dont use a closure as a callable. order is a crude order control mechanism. order defaults to the integer 0; it can be set to any other integer. All actions that share an order will be called before other actions that share a higher order. This makes it possible to write a directive with callable logic that relies on the execution of the callable of another directive being done rst. For example, Pyramids pyramid.config.Configurator.add_view() directive registers an action with a higher order than the pyramid.config.Configurator.add_route() method. Due to this, the add_view methods callable can assume that, if a route_name was passed to it, that a route by this name was already registered by add_route, and if such a route has not already been registered, its a conguration error (a view that names a nonexistent route via its route_name parameter will never be called). introspectables is a sequence of introspectable objects. You can pass a sequence of introspectables to the action() method, which allows you to augment Pyramids conguration introspection system. 378
Pyramid provides a conguration introspection system that can be used by debugging tools to provide visibility into the conguration of a running application. All built-in Pyramid directives (such as pyramid.config.Configurator.add_view() and pyramid.config.Configurator.add_route()) register a set of introspectables when called. For example, when you register a view via add_view, the directive registers at least one introspectable: an introspectable about the view registration itself, providing human-consumable values for the arguments it was passed. You can later use the introspection query system to determine whether a particular view uses a renderer, or whether a particular view is limited to a particular request method, or which routes a particular view is registered against. The Pyramid debug toolbar makes use of the introspection system in various ways to display information to Pyramid developers. Introspection values are set when a sequence of introspectable objects is passed to the action() method. Heres an example of a directive which uses introspectables:
1 2 3 4 5 6 7 8 9 10 11 12 13
def add_jammyjam(config, value): def register(): config.registry.jammyjam = value intr = config.introspectable(category_name=jammyjams, discriminator=jammyjam, title=a jammyjam, type_name=None) intr[value] = value config.action(jammyjam, register, introspectables=(intr,)) if __name__ == __main__: config = Configurator() config.add_directive(add_jammyjam, add_jammyjam)
If you notice, the above directive uses the introspectable attribute of a Congurator (pyramid.config.Configurator.introspectable) to create an introspectable object. The introspectable objects constructor requires at least four arguments: the category_name, the discriminator, the title, and the type_name. The category_name is a string representing the logical category for this introspectable. Usually the category_name is a pluralization of the type of object being added via the action. 379
34. EXTENDING PYRAMID CONFIGURATION The discriminator is a value unique within the category (unlike the action discriminator, which must be unique within the entire set of actions). It is typically a string or tuple representing the values unique to this introspectable within the category. It is used to generate links and as part of a relationshipforming target for other introspectables. The title is a human-consumable string that can be used by introspection system frontends to show a friendly summary of this introspectable. The type_name is a value that can be used to subtype this introspectable within its category for sorting and presentation purposes. It can be any value. An introspectable is also dictionary-like. It can contain any set of key/value pairs, typically related to the arguments passed to its related directive. While the category_name, discriminator, title and type_name are metadata about the introspectable, the values provided as key/value pairs are the actual data provided by the introspectable. In the above example, we set the value key to the value of the value argument passed to the directive. Our directive above mutates the introspectable, and passes it in to the action method as the rst element of a tuple as the value of the introspectable keyword argument. This associates this introspectable with the action. Introspection tools will then display this introspectable in their index.
def add_jammyjam(config, value, template): def register(): config.registry.jammyjam = (value, template) intr = config.introspectable(category_name=jammyjams, discriminator=jammyjam, title=a jammyjam, type_name=None) intr[value] = value tmpl_intr = config.introspectable(category_name=jammyjam templates, discriminator=template, title=template, type_name=None) tmpl_intr[value] = template intr.relate(jammyjam templates, template) config.action(jammyjam, register, introspectables=(intr, tmpl_intr)) if __name__ == __main__: config = Configurator() config.add_directive(add_jammyjam, add_jammyjam)
380
34.3. ADDING CONFIGURATION INTROSPECTION In the above example, the add_jammyjam directive registers two introspectables. The rst is related to the value passed to the directive; the second is related to the template passed to the directive. If you believe a concept within a directive is important enough to have its own introspectable, you can cause the same directive to register more than one introspectable, registering one introspectable for the main idea and another for a related concept. The call to intr.relate above (pyramid.interfaces.IIntrospectable.relate()) is passed two arguments: a category name and a directive. The example above effectively indicates that the directive wishes to form a relationship between the intr introspectable and the tmpl_intr introspectable; the arguments passed to relate are the category name and discriminator of the tmpl_intr introspectable. Relationships need not be made between two introspectables created by the same directive. Instead, a relationship can be formed between an introspectable created in one directive and another introspectable created in another by calling relate on either side with the other directives category name and discriminator. An error will be raised at conguration commit time if you attempt to relate an introspectable with another nonexistent introspectable, however. Introspectable relationships will show up in frontend system renderings of introspection values. For example, if a view registration names a route name, the introspectable related to the view callable will show a reference to the route to which it relates to and vice versa.
381
382
CHAPTER
THIRTYFIVE
THREAD LOCALS
A thread local variable is a variable that appears to be a global variable to an application which uses it. However, unlike a true global variable, one thread or process serving the application may receive a different value than another thread or process when that variable is thread local. When a request is processed, Pyramid makes two thread local variables available to the application: a registry and a request.
35. THREAD LOCALS current request; the latter returns the current registry. Both get_current_* functions retrieve an object from a thread-local data structure. These API functions are documented in pyramid.threadlocal. These values are thread locals rather than true globals because one Python process may be handling multiple simultaneous requests or even multiple Pyramid applications. If they were true globals, Pyramid could not handle multiple simultaneous requests or allow more than one Pyramid application instance to exist in a single Python process. Because one Pyramid application is permitted to call another Pyramid application from its own view code (perhaps as a WSGI app with help from the pyramid.wsgi.wsgiapp2() decorator), these variables are managed in a stack during normal system operations. The stack instance itself is a threading.local. During normal operations, the thread locals stack is managed by a Router object. At the beginning of a request, the Router pushes the applications registry and the request on to the stack. At the end of a request, the stack is popped. The topmost request and registry on the stack are considered current. Therefore, when the system is operating normally, the very denition of current is dened entirely by the behavior of a pyramid Router. However, during unit testing, no Router code is ever invoked, and the denition of current is dened by the boundary between calls to the pyramid.config.Configurator.begin() and pyramid.config.Configurator.end() methods (or between calls to the pyramid.testing.setUp() and pyramid.testing.tearDown() functions). These functions push and pop the threadlocal stack when the system is under test. See Test Set Up and Tear Down for the denitions of these functions. Scripts which use Pyramid machinery but never actually start a WSGI server or receive requests via HTTP such as scripts which use the pyramid.scripting API will never cause any Router code to be executed. However, the pyramid.scripting APIs also push some values on to the thread locals stack as a matter of course. Such scripts should expect the get_current_request() function to always return None, and should expect the get_current_registry() function to return exactly the same application registry for every request.
384
35.2. WHY YOU SHOULDNT ABUSE THREAD LOCALS get_current_request should never be called within the body of a view callable, or within code called by a view callable. View callables already have access to the request (its passed in to each as request). get_current_request should never be called in resource code. If a resource needs access to the request, it should be passed the request by a view callable. get_current_request function should never be called because its easier or more elegant to think about calling it than to pass a request through a series of function calls when creating some API design. Your application should instead almost certainly pass data derived from the request around rather than relying on being able to call this function to obtain the request in places that actually have no business knowing about it. Parameters are meant to be passed around as function arguments, this is why they exist. Dont try to save typing or create nicer APIs by using this function in the place where a request is required; this will only lead to sadness later. Neither get_current_request nor get_current_registry should ever be called within application-specic forks of third-party library code. The library youve forked almost certainly has nothing to do with Pyramid, and making it dependent on Pyramid (rather than making your pyramid application depend upon it) means youre forming a dependency in the wrong direction. Use of the get_current_request() function in application code is still useful in very limited circumstances. As a rule of thumb, usage of get_current_request is useful within code which is meant to eventually be removed. For instance, you may nd yourself wanting to deprecate some API that expects to be passed a request object in favor of one that does not expect to be passed a request object. But you need to keep implementations of the old API working for some period of time while you deprecate the older API. So you write a facade implementation of the new API which calls into the code which implements the older API. Since the new API does not require the request, your facade implementation doesnt have local access to the request when it needs to pass it into the older API implementation. After some period of time, the older implementation code is disused and the hack that uses get_current_request is removed. This would be an appropriate place to use the get_current_request. Use of the get_current_registry() function should be limited to testing scenarios. The registry made current by use of the pyramid.config.Configurator.begin() method during a test (or via pyramid.testing.setUp()) when you do not pass one in is available to you via this API.
385
386
CHAPTER
THIRTYSIX
from pyramid.interfaces import ISettings from zope.component import getUtility settings = getUtility(ISettings)
After this code runs, settings will be a Python dictionary. But its unlikely that any civilian will be able to gure this out just by reading the code casually. When the zope.component.getUtility API is used by a developer, the conceptual load on a casual reader of code is high. While the ZCA is an excellent tool with which to build a framework such as Pyramid, it is not always the best tool with which to build an application due to the opacity of the zope.component APIs. Accordingly, Pyramid tends to hide the presence of the ZCA from application developers. You neednt understand the ZCA to create a Pyramid application; its use is effectively only a framework implementation detail. However, developers who are already used to writing Zope applications often still wish to use the ZCA while building a Pyramid application; pyramid makes this possible. 387
388
registry.getUtility(IFoo)
The full method API is documented in the zope.component package, but it largely mirrors the global API almost exactly. If you are willing to disuse the global ZCA APIs and use the method interface of a registry instead, you need only know how to obtain the Pyramid component registry. There are two ways of doing so: use the pyramid.threadlocal.get_current_registry() function within Pyramid view or resource code. This will always return the current Pyramid application registry. use the attribute of the request object named registry in your Pyramid view code, eg. request.registry. This is the ZCA component registry related to the running Pyramid application. See Thread Locals for more information about pyramid.threadlocal.get_current_registry().
from zope.component import getGlobalSiteManager from pyramid.config import Configurator def app(global_settings, **settings): config = Configurator(settings=settings) config.include(some.other.package) return config.make_wsgi_app()
When the app function above is run, a Congurator is constructed. When the congurator is created, it creates a new application registry (a ZCA component registry). A new registry is constructed whenever the registry argument is omitted when a Congurator constructor is called, or when a registry argument with a value of None is passed to a Congurator constructor. During a request, the application registry created by the Congurator is made current. This means calls to get_current_registry() in the thread handling the request will return the component registry associated with the application. 389
36. USING THE ZOPE COMPONENT ARCHITECTURE IN PYRAMID As a result, application developers can use get_current_registry to get the registry and thus get access to utilities and such, as per Disusing the Global ZCA API . But they still cannot use the global ZCA API. Without special treatment, the ZCA global APIs will always return the global ZCA registry (the one in zope.component.globalregistry.base). To x this and make the ZCA global APIs use the current Pyramid registry, you need to call hook_zca() within your setup code. For example:
1 2 3 4 5 6 7 8
from zope.component import getGlobalSiteManager from pyramid.config import Configurator def app(global_settings, **settings): config = Configurator(settings=settings) config.hook_zca() config.include(some.other.application) return config.make_wsgi_app()
Weve added a line to our original startup code, line number 6, which calls config.hook_zca(). The effect of this line under the hood is that an analogue of the following code is executed:
1 2 3
This causes the ZCA global API to start using the Pyramid application registry in threads which are running a Pyramid request. Calling hook_zca is usually sufcient to x the problem of being able to use the global ZCA API within a Pyramid application. However, it also means that a Zope application that is running in the same process may start using the Pyramid global registry instead of the Zope global registry, effectively inverting the original problem. In such a case, follow the steps in the next section, Enabling the ZCA Global API by Using The ZCA Global Registry.
36.1.3 Enabling the ZCA Global API by Using The ZCA Global Registry
You can tell your Pyramid application to use the ZCA global registry at startup time instead of constructing a new one:
390
1 2 3 4 5 6 7 8 9
from zope.component import getGlobalSiteManager from pyramid.config import Configurator def app(global_settings, **settings): globalreg = getGlobalSiteManager() config = Configurator(registry=globalreg) config.setup_registry(settings=settings) config.include(some.other.application) return config.make_wsgi_app()
Lines 5, 6, and 7 above are the interesting ones. Line 5 retrieves the global ZCA component registry. Line 6 creates a Congurator, passing the global ZCA registry into its constructor as the registry argument. Line 7 sets up the global registry with Pyramid-specic registrations; this is code that is normally executed when a registry is constructed rather than created, but we must call it by hand when we pass an explicit registry. At this point, Pyramid will use the ZCA global registry rather than creating a new application-specic registry; since by default the ZCA global API will use this registry, things will work as you might expect a Zope app to when you use the global ZCA API.
391
392
Part II
Tutorials
CHAPTER
THIRTYSEVEN
37.1 Background
This version of the Pyramid wiki tutorial presents a Pyramid application that uses technologies which will be familiar to someone with Zope experience. It uses ZODB as a persistence mechanism and traversal to map URLs to code. It can also be followed by people without any prior Python web framework experience. To code along with this tutorial, the developer will need a UNIX machine with development tools (Mac OS X with XCode, any Linux or BSD variant, etc) or a Windows system of any kind.
This tutorial has been written for Python 2. It is unlikely to work without modication under Python 3. Have fun! 395
37.2 Design
Following is a quick overview of our wiki application, to help us understand the changes that we will be doing next in our default les generated by the zodb scafffold.
37.2.1 Overall
We choose to use reStructuredText markup in the wiki text. Translation from reStructuredText to HTML is provided by the widely used docutils Python module. We will add this module in the dependency list on the project setup.py le.
37.2.2 Models
The root resource, named Wiki, will be a mapping of wiki page names to page resources. The page resources will be instances of a Page class and they store the text content. URLs like /PageName will be traversed using Wiki[ PageName ] => page, and the context that results is the page resource of an existing page. To add a page to the wiki, a new instance of the page resource is created and its name and reference are added to the Wiki mapping. A page named FrontPage containing the text This is the front page, will be created when the storage is initialized, and will be used as the wiki home page.
37.2.3 Views
There will be three views to handle the normal operations of adding, editing and viewing wiki pages, plus one view for the wiki front page. Two templates will be used, one for viewing, and one for both for adding and editing wiki pages. The default templating systems in Pyramid are Chameleon and Mako. Chameleon is a variant of ZPT , which is an XML-based templating language. Mako is a non-XML-based templating language. Because we had to pick one, we chose Chameleon for this tutorial.
396
37.2. DESIGN
37.2.4 Security
Well eventually be adding security to our application. The components well use to do this are below. USERS, a dictionary mapping usernames to their corresponding passwords. GROUPS, a dictionary mapping usernames to a list of groups they belong to. groupfinder, an authorization callback that looks up USERS and GROUPS. It will be provided in a new security.py le. An ACL is attached to the root resource. Each row below details an ACE: Action Allow Allow Principal Everyone group:editors Permission View Edit
Permission declarations are added to the views to assert the security policies as each request is handled. Two additional views and one template will handle the login and logout tasks.
37.2.5 Summary
The URL, context, actions, template and permission associated to each view are listed in the following table:
397
37. ZODB + TRAVERSAL WIKI TUTORIAL URL / /PageName View view_wiki view_page 1 Context Wiki Page Page Action Redirect to /FrontPage Display existing page 2 Display edit form with existing content. If the form was submitted, redirect to /PageName Create the page PageName in storage, display the edit form without content. If the form was submitted, redirect to /PageName Display login form. If the form was submitted, authenticate. If authentication successful, redirect to the page that we came from. If authentication fails, display login form with login failed message. Redirect /FrontPage to Template Permission
view.pt edit.pt
view edit
/PageName/edit_page edit_page
/add_page/PageName add_page
Wiki
edit.pt
edit
/login
login
Wiki, Forbidden
3
login.pt
/logout
logout
Wiki 398
37.3. INSTALLATION
37.3 Installation
37.3.1 Preparation
Follow the steps in Installing Pyramid, but name the virtualenv directory pyramidtut. Preparation, UNIX 1. Switch to the pyramidtut directory:
$ cd pyramidtut
On Windows:
c:\pyramidtut> Scripts\pcreate -s zodb tutorial
You dont have to call it tutorial the code uses relative paths for imports and nding templates and static resources.
If you are using Windows, the zodb scaffold doesnt currently deal gracefully with installation into a location that contains spaces in the path. If you experience startup problems, try putting both the virtualenv and the project into directories that do not contain spaces in their paths.
On Windows:
C:\pyramidtut> cd tutorial C:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
37.3. INSTALLATION
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\nosetests --cover-package=tutorial ^ --cover-erase --with-coverage
Looks like the code in the zodb scaffold for ZODB projects is missing some test coverage, particularly in the le named models.py.
On Windows:
401
Your OS rewall, if any, may pop up a dialog asking for authorization to allow python to accept incoming network connections.
Pyramid supports any persistent storage mechanism (e.g. a SQL database or lesystem les, etc). Pyramid also supports an additional mechanism to map URLs to code (URL dispatch). However, for the purposes of this tutorial, well only be using traversal and ZODB.
from pyramid.config import Configurator from pyramid_zodbconn import get_connection from .models import appmaker
def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ config = Configurator(root_factory=root_factory, settings=settings) config.add_static_view(static, static, cache_max_age=3600) config.scan() return config.make_wsgi_app()
1. Lines 1-3. Perform some dependency imports. 2. Lines 6-8 Dene a root factory for our Pyramid application. 3. Line 14. We construct a Congurator with a root factory and the settings keywords parsed by PasteDeploy. The root factory is named root_factory. 4. Line 15. Register a static view which answers requests which start with URL path /static using the pyramid.config.Configurator.add_static_view method(). This statement registers a view that will serve up static assets, such as CSS and image les, for us, in this case, at http://localhost:6543/static/ and below. The rst argument is the name static, which indicates that the URL path prex of the view will be /static. The second argument of this tag is the path, which is a relative asset specication, so it nds the resources it should serve within the static directory inside the tutorial package. The scaffold could have alternately used an absolute asset specication as the path (tutorial:static) but it does not. 403
37. ZODB + TRAVERSAL WIKI TUTORIAL 5. Line 16. Perform a scan. A scan will nd conguration decoration, such as view conguration decorators (e.g. @view_config) in the source code of the tutorial package and will take actions based on these decorators. We dont pass any arguments to scan(), which implies that the scan should take place in the current package (in this case, tutorial). The scaffold could have equivalently said config.scan(tutorial) but it chose to omit the package name argument. 6. Line 17. Use the pyramid.config.Configurator.make_wsgi_app() method to return a WSGI application.
def appmaker(zodb_root): if not app_root in zodb_root: app_root = MyModel() zodb_root[app_root] = app_root import transaction transaction.commit() return zodb_root[app_root]
1. Lines 4-5. The MyModel resource class is implemented here. Instances of this class will be capable of being persisted in ZODB because the class inherits from the persistent.mapping.PersistentMapping class. The __parent__ and __name__ are important parts of the traversal protocol. By default, have these as None indicating that this is the root object.
404
37.4. BASIC LAYOUT 2. Lines 8-14. appmaker is used to return the application root object. It is called on every request to the Pyramid application. It also performs bootstrapping by creating an application root (inside the ZODB root object) if one does not already exist. It is used by the root_factory weve dened in our __init__.py. We do so by rst seeing if the database has the persistent application root. If not, we make an instance, store it, and commit the transaction. We then return the application root object.
Lets try to understand the components in this module: 1. Lines 1-2. Perform some dependency imports. 2. Line 5. Use the pyramid.view.view_config() conguration decoration to perform a view conguration registration. This view conguration registration will be activated when the application is started. It will be activated by virtue of it being found as the result of a scan (when Line 14 of __init__.py is run). The @view_config decorator accepts a number of keyword arguments. We use two keyword arguments here: context and renderer. The context argument signies that the decorated view callable should only be run when traversal nds the tutorial.models.MyModel resource to be the context of a request. In English, this means that when the URL / is visited, because MyModel is the root model, this view callable will be invoked. The renderer argument names an asset specication of templates/mytemplate.pt. This asset specication points at a Chameleon template which lives in the mytemplate.pt le 405
37. ZODB + TRAVERSAL WIKI TUTORIAL within the templates directory of the tutorial package. And indeed if you look in the templates directory of this package, youll see a mytemplate.pt template le, which renders the default home page of the generated project. This asset specication is relative (to the view.pys current package). We could have alternately an used the absolute asset specication tutorial:templates/mytemplate.pt, but chose to use the relative version. Since this call to @view_config doesnt pass a name argument, the my_view function which it decorates represents the default view callable used when the context is of the type MyModel. 3. Lines 6-7. We dene a view callable named my_view, which we decorated in the step above. This view callable is a function we write generated by the zodb scaffold that is given a request and which returns a dictionary. The mytemplate.pt renderer named by the asset specication in the step above will convert this dictionary to a response on our behalf. The function returns the dictionary {project:tutorial}. This dictionary is used by the template named by the mytemplate.pt asset specication to ll in certain values on the page.
406
# By default, the 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 port = 6543 ### # logging configuration # http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html ### [loggers] keys = root, tutorial [handlers] keys = console [formatters] keys = generic [logger_root] level = INFO handlers = console [logger_tutorial] level = DEBUG handlers = qualname = tutorial [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
Note the existence of an [app:main] section which species our WSGI application. Our ZODB 407
37. ZODB + TRAVERSAL WIKI TUTORIAL database settings are specied as the zodbconn.uri setting within this section. This value, and the other values within this section are passed as **settings to the main function we dened in __init__.py when the server is started via pserve.
There is nothing automagically special about the lename models.py. A project may have many models throughout its codebase in arbitrarily-named les. Files implementing models often have model in their lenames, or they may live in a Python subpackage of your application package named models, but this is only by convention.
The rst thing we want to do is remove the MyModel class from the generated models.py le. The MyModel class is only a sample and were not going to use it. 408
37.5. DEFINING THE DOMAIN MODEL Then, well add a Wiki class. We want it to inherit from the persistent.mapping.PersistentMapping class because it provides mapping behavior, and it makes sure that our Wiki page is stored as a rst-class persistent object in our ZODB database. Our Wiki class should have two attributes set to None at class scope: __parent__ and __name__. If a model has a __parent__ attribute of None in a traversal-based Pyramid application, it means that its the root model. The __name__ of the root model is also always None. Then well add a Page class. This class should inherit from the persistent.Persistent class. Well also give it an __init__ method that accepts a single parameter named data. This parameter will contain the ReStructuredText body representing the wiki page content. Note that Page objects dont have an initial __name__ or __parent__ attribute. All objects in a traversal graph must have a __name__ and a __parent__ attribute. We dont specify these here because both __name__ and __parent__ will be set by a view function when a Page is added to our Wiki mapping. As a last step, we want to change the appmaker function in our models.py le so that the root resource of our application is a Wiki instance. Well also slot a single page object (the front page) into the Wiki within the appmaker. This will provide traversal a resource tree to work against when it attempts to resolve URLs to resources.
from persistent import Persistent from persistent.mapping import PersistentMapping class Wiki(PersistentMapping): __name__ = None __parent__ = None class Page(Persistent): def __init__(self, data): self.data = data def appmaker(zodb_root): if not app_root in zodb_root: app_root = Wiki() frontpage = Page(This is the front page) app_root[FrontPage] = frontpage frontpage.__name__ = FrontPage frontpage.__parent__ = app_root zodb_root[app_root] = app_root
409
20 21 22
A Pyramid view can also be dened as callable which accepts only a request argument. Youll see this one-argument pattern used in other Pyramid tutorials and applications. Either calling convention will work in any Pyramid application; the calling conventions can be used interchangeably as necessary. In traversal based applications, URLs are mapped to a context resource, and since our resource tree also represents our applications domain model, were often interested in the context, because it represents the persistent storage of our application. For this reason, in this tutorial we dene views as callables that accept context in the callable argument list. If you do need the context within a view function that only takes the request as a single argument, you can obtain it via request.context.
Were going to dene several view callable functions, then wire them into Pyramid using some view conguration. The source code for this tutorial stage can be browsed via http://github.com/Pylons/pyramid/tree/1.3branch/docs/tutorials/wiki/src/views/.
410
import os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, README.txt)).read() CHANGES = open(os.path.join(here, CHANGES.txt)).read() requires = [ pyramid, pyramid_zodbconn, transaction, pyramid_tm, pyramid_debugtoolbar, ZODB3, waitress, docutils, ] setup(name=tutorial, version=0.0, description=tutorial, long_description=README + \n\n + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author=, author_email=, url=, keywords=web pylons pyramid, packages=find_packages(), include_package_data=True, zip_safe=False,
411
37 38 39 40 41 42 43 44
After these new dependencies are added, you will need to rerun python setup.py develop inside the root of the tutorial package to obtain and register the newly added dependency package.
There is nothing special about the lename views.py. A project may have many view callables throughout its codebase in arbitrarily-named les. Files implementing view callables often have view in their lenames (or may live in a Python subpackage of your application package named views), but this is only by convention.
The view_wiki view function The view_wiki function will be congured to respond as the default view callable for a Wiki resource. Well provide it with a @view_config decorator which names the class tutorial.models.Wiki as its context. This means that when a Wiki resource is the context, and no view name exists in the request, this view will be used. The view conguration associated with view_wiki does not use a renderer because the view callable always returns a response object rather than a dictionary. No renderer is necessary when a view returns a response object. The view_wiki view callable source named FrontPage. always redirects to the URL To do so, it returns an 412 of a Page instance of rethe
37.6. DEFINING VIEWS pyramid.httpexceptions.HTTPFound class (instances of which implement the pyramid.interfaces.IResponse interface like pyramid.response.Response does). The pyramid.request.Request.resource_url() API. pyramid.request.Request.resource_url() constructs a URL to the FrontPage page resource (e.g. http://localhost:6543/FrontPage), and uses it as the location of the HTTPFound response, forming an HTTP redirect.
The view_page view function The view_page function will be congured to respond as the default view of a Page resource. Well provide it with a @view_config decorator which names the class tutorial.models.Page as its context. This means that when a Page resource is the context, and no view name exists in the request, this view will be used. We inform Pyramid this view will use the templates/view.pt template le as a renderer. The view_page function generates the ReStructuredText body of a page (stored as the data attribute of the context passed to the view; the context will be a Page resource) as HTML. Then it substitutes an HTML anchor for each WikiWord reference in the rendered HTML using a compiled regular expression. The curried function named check is used as the rst argument to wikiwords.sub, indicating that it should be called to provide a value for each WikiWord match found in the content. If the wiki (our pages __parent__) already contains a page with the matched WikiWord name, the check function generates a view link to be used as the substitution value and returns it. If the wiki does not already contain a page with the matched WikiWord name, the function generates an add link as the substitution value and returns it. As a result, the content variable is now a fully formed bit of HTML containing various view and add links for WikiWords based on the content of our current page resource. We then generate an edit URL (because its easier to do here than in the template), and we wrap up a number of arguments in a dictionary and return it. The arguments we wrap into a dictionary include page, content, and edit_url. As a result, the template associated with this view callable (via renderer= in its conguration) will be able to use these names to perform various rendering tasks. The template associated with this view callable will be a template which lives in templates/view.pt. Note the contrast between this view callable and the view_wiki view callable. In the view_wiki view callable, we unconditionally return a response object. In the view_page view callable, we return a dictionary. It is always ne to return a response object from a Pyramid view. Returning a dictionary is allowed only when there is a renderer associated with the view callable in the view conguration. 413
37. ZODB + TRAVERSAL WIKI TUTORIAL The add_page view function The add_page function will be congured to respond when the context resource is a Wiki and the view name is add_page. Well provide it with a @view_config decorator which names the string add_page as its view name (via name=), the class tutorial.models.Wiki as its context, and the renderer named templates/edit.pt. This means that when a Wiki resource is the context, and a view name named add_page exists as the result of traversal, this view will be used. We inform Pyramid this view will use the templates/edit.pt template le as a renderer. We share the same template between add and edit views, thus edit.pt instead of add.pt. The add_page function will be invoked when a user clicks on a WikiWord which isnt yet represented as a page in the system. The check function within the view_page view generates URLs to this view. It also acts as a handler for the form that is generated when we want to add a page resource. The context of the add_page view is always a Wiki resource (not a Page resource). The request subpath in Pyramid is the sequence of names that are found after the view name in the URL segments given in the PATH_INFO of the WSGI request as the result of traversal. If our add view is invoked via, e.g. http://localhost:6543/add_page/SomeName, the subpath will be a tuple: (SomeName,). The add view takes the zeroth element of the subpath (the wiki page name), and aliases it to the name attribute in order to know the name of the page were trying to add. If the view rendering is not a result of a form submission (if the expression form.submitted in request.params is False), the view renders a template. To do so, it generates a save url which the template use as the form post URL during rendering. Were lazy here, so were trying to use the same template (templates/edit.pt) for the add view as well as the page edit view. To do so, we create a dummy Page resource object in order to satisfy the edit forms desire to have some page object exposed as page, and well render the template to a response. If the view rendering is a result of a form submission (if the expression form.submitted in request.params is True), we scrape the page body from the form data, create a Page object using the name in the subpath and the page body, and save it into our context (the Wiki) using the __setitem__ method of the context. We then redirect back to the view_page view (the default view for a page) for the newly created page.
The edit_page view function The edit_page function will be congured to respond when the context is a Page resource and the view name is edit_page. Well provide it with a @view_config decorator which names the string edit_page as its view name (via name=), the class tutorial.models.Page as its context, and 414
37.6. DEFINING VIEWS the renderer named templates/edit.pt. This means that when a Page resource is the context, and a view name exists as the result of traversal named edit_page, this view will be used. We inform Pyramid this view will use the templates/edit.pt template le as a renderer. The edit_page function will be invoked when a user clicks the Edit this Page button on the view form. It renders an edit form but it also acts as the form post view callable for the form it renders. The context of the edit_page view will always be a Page resource (never a Wiki resource). If the view execution is not a result of a form submission (if the expression form.submitted in request.params is False), the view simply renders the edit form, passing the page resource, and a save_url which will be used as the action of the generated form. If the view execution is a result of a form submission (if the expression form.submitted in request.params is True), the view grabs the body element of the request parameter and sets it as the data attribute of the page context. It then redirects to the default view of the context (the page), which will always be the view_page view.
from docutils.core import publish_parts import re from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config from .models import Page # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @view_config(context=.models.Wiki) def view_wiki(context, request): return HTTPFound(location=request.resource_url(context, FrontPage)) @view_config(context=.models.Page, renderer=templates/view.pt) def view_page(context, request): wiki = context.__parent__ def check(match): word = match.group(1) if word in wiki:
415
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
page = wiki[word] view_url = request.resource_url(page) return <a href="%s">%s</a> % (view_url, word) else: add_url = request.application_url + /add_page/ + word return <a href="%s">%s</a> % (add_url, word) content = publish_parts(context.data, writer_name=html)[html_body] content = wikiwords.sub(check, content) edit_url = request.resource_url(context, edit_page) return dict(page = context, content = content, edit_url = edit_url) @view_config(name=add_page, context=.models.Wiki, renderer=templates/edit.pt) def add_page(context, request): pagename = request.subpath[0] if form.submitted in request.params: body = request.params[body] page = Page(body) page.__name__ = pagename page.__parent__ = context context[pagename] = page return HTTPFound(location = request.resource_url(page)) save_url = request.resource_url(context, add_page, pagename) page = Page() page.__name__ = pagename page.__parent__ = context return dict(page = page, save_url = save_url) @view_config(name=edit_page, context=.models.Page, renderer=templates/edit.pt) def edit_page(context, request): if form.submitted in request.params: context.data = request.params[body] return HTTPFound(location = request.resource_url(context)) return dict(page=context, save_url=request.resource_url(context, edit_page))
37.6. DEFINING VIEWS The view.pt Template Create tutorial/tutorial/templates/view.pt and add the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="/static/favicon.ico" /> <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="/static/pyramid-small.png" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Viewing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/> You can return to the <a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"></div> </div> </div>
417
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
<div id="bottom"> <div class="bottom"> <div tal:replace="structure content"> Page text goes here. </div> <p> <a tal:attributes="href edit_url" href=""> Edit this page </a> </p> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
This template is used by view_page() for displaying a single wiki page. It includes: A div element that is replaced with the content value provided by the view (rows 45-47). content contains HTML, so the structure keyword is used to prevent escaping it (i.e. changing > to >, etc.) A link that points at the edit URL which invokes the edit_page view for the page being viewed (rows 49-51).
The edit.pt Template Create tutorial/tutorial/templates/edit.pt and add the following content:
1 2 3 4 5 6 7 8 9 10
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" />
418
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
<link rel="shortcut icon" href="/static/favicon.ico" /> <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="/static/pyramid-small.png" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Editing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/> You can return to the <a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"></div> </div> </div> <div id="bottom"> <div class="bottom"> <form action="${save_url}" method="post"> <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/> <input type="submit" name="form.submitted" value="Save"/> </form> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div>
419
57 58
</body> </html>
This template is used by add_page() and edit_page() for adding and editing a wiki page. It displays a page containing a form that includes: A 10 row by 60 column textarea eld named body that is lled with any existing page data when it is rendered (rows 46-47). A submit button that has the name form.submitted (row 48). The form POSTs back to the save_url argument supplied by the view (row 45). The view will use the body and form.submitted values.
Our templates use a request object that none of our tutorial views return in their dictionary. request is one of several names that are available by default in a template when a template renderer is used. See *.pt or *.txt: Chameleon Template Renderers for information about other names that are available by default when a Chameleon template is used as a renderer.
Static Assets Our templates name a single static asset named pylons.css. We dont need to create this le within our packages static directory because it was provided at the time we created the project. This le is a little too long to replicate within the body of this guide, however it is available online. This CSS le will be accessed via e.g. /static/pylons.css by virtue of the call to add_static_view directive weve made in the __init__.py le. Any number and type of static assets can be placed in this directory (or subdirectories) and are just referred to by URL.
37.7. ADDING AUTHORIZATION http://localhost:6543/FrontPage/ invokes the view_page view of the front page resource. This is because its the default view (a view without a name) for Page resources. http://localhost:6543/FrontPage/edit_page invokes the edit view for the FrontPage Page resource. http://localhost:6543/add_page/SomePageName invokes the add view for a Page. To generate an error, visit http://localhost:6543/add_page which will generate an IndexErrorr: tuple index out of range error. Youll see an interactive traceback facility provided by pyramid_debugtoolbar.
USERS = {editor:editor, viewer:viewer} GROUPS = {editor:[group:editors]} def groupfinder(userid, request): if userid in USERS: return GROUPS.get(userid, [])
The groupfinder function accepts a userid and a request and returns one of these values: If the userid exists in the system, it will return a sequence of group identiers (or an empty sequence if the user isnt a member of any groups). If the userid does not exist in the system, it will return None. For example, groupfinder(editor, request ) returns [group:editor], groupfinder(viewer, request) returns [], and groupfinder(admin, request) returns None. We will use groupfinder() as an authentication policy callback that will provide the principal or principals for a user. In a production system, user and group data will most often come from a database, but here we use dummy data to represent user and groups sources.
Add an ACL Open tutorial/tutorial/models.py and add the following import statement at the head:
1 2 3 4
422
1 2 3 4 5
class Wiki(PersistentMapping): __name__ = None __parent__ = None __acl__ = [ (Allow, Everyone, view), (Allow, group:editors, edit) ]
We import Allow, an action that means that permission is allowed:, and Everyone, a special principal that is associated to all requests. Both are used in the ACE entries that make up the ACL. The ACL is a list that needs to be named __acl__ and be an attribute of a class. We dene an ACL with two ACE entries: the rst entry allows any user the view permission. The second entry allows the group:editors principal the edit permission. The Wiki class that contains the ACL is the resource constructor for the root resource, which is a Wiki instance. The ACL is provided to each view in the context of the request, as the context attribute. Its only happenstance that were assigning this ACL at class scope. An ACL can be attached to an object instance too; this is how row level security can be achieved in Pyramid applications. We actually only need one ACL for the entire system, however, because our security requirements are simple, so this feature is not demonstrated. See Assigning ACLs to your Resource Objects for more information about what an ACL represents.
Add Authentication and Authorization Policies Open tutorial/__init__.py and add these import statements:
1 2 3
from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy from .security import groupfinder
authn_policy = AuthTktAuthenticationPolicy( sosecret, callback=groupfinder, hashalg=sha512) authz_policy = ACLAuthorizationPolicy() config = Configurator(root_factory=root_factory, settings=settings) config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy)
423
37. ZODB + TRAVERSAL WIKI TUTORIAL (Only the highlighted lines need to be added.) We are enabling an AuthTktAuthenticationPolicy, it is based in an auth ticket that may be included in the request, and an ACLAuthorizationPolicy that uses an ACL to determine the allow or deny outcome for a view. Note that the pyramid.authentication.AuthTktAuthenticationPolicy constructor accepts two arguments: secret and callback. secret is a string representing an encryption key used by the authentication ticket machinery represented by this policy: it is required. The callback is the groupfinder() function that we created before.
Add permission declarations Add a permission=edit parameter to the @view_config decorator for add_page() and edit_page(), for example:
1 2
(Only the highlighted line needs to be added.) The result is that only users who possess the edit permission at the time of the request may invoke those two views. Add a permission=view parameter to the @view_config decorator for view_wiki() and view_page(), like this:
1 2
(Only the highlighted line needs to be added.) This allows anyone to invoke these two views. We are done with the changes needed to control access. The changes that follow will add the login and logout feature.
424
from pyramid.view import ( view_config, forbidden_view_config, ) from pyramid.security import ( remember, forget, ) from .security import USERS
(Only the highlighted lines need to be added.) forbidden_view_config() will be used to customize the default 403 Forbidden page. remember() and forget() help to create and expire an auth ticket cookie. Now add the login and logout views:
1 2 3 4 5 6 7 8 9 10 11 12
@view_config(context=.models.Wiki, name=login, renderer=templates/login.pt) @forbidden_view_config(renderer=templates/login.pt) def login(request): login_url = request.resource_url(request.context, login) referrer = request.url if referrer == login_url: referrer = / # never use the login form itself as came_from came_from = request.params.get(came_from, referrer) message = login = password =
425
13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
if form.submitted in request.params: login = request.params[login] password = request.params[password] if USERS.get(login) == password: headers = remember(request, login) return HTTPFound(location = came_from, headers = headers) message = Failed login return dict( message = message, url = request.application_url + /login, came_from = came_from, login = login, password = password, ) @view_config(context=.models.Wiki, name=logout) def logout(request): headers = forget(request) return HTTPFound(location = request.resource_url(request.context), headers = headers)
login() is decorated with two decorators: a @view_config decorator which associates it with the login route and makes it visible when we visit /login, a @forbidden_view_config decorator which turns it into an forbidden view. login() will be invoked when a users tries to execute a view callable that they are not allowed to. For example, if a user has not logged in and tries to add or edit a Wiki page, he will be shown the login form before being allowed to continue on. The order of these two view conguration decorators is unimportant. logout() is decorated with a @view_config decorator which associates it with the logout route. It will be invoked when we visit /logout.
Add the login.pt Template Create tutorial/tutorial/templates/login.pt with the following content:
426
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="/static/favicon.ico" /> <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="/static/pyramid-small.png" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> <b>Login</b><br/> <span tal:replace="message"/> </div> <div id="right" class="app-welcome align-right"></div> </div> </div> <div id="bottom"> <div class="bottom"> <form action="${url}" method="post"> <input type="hidden" name="came_from" value="${came_from}"/> <input type="text" name="login" value="${login}"/><br/> <input type="password" name="password"
427
value="${password}"/><br/> <input type="submit" name="form.submitted" value="Log In"/> </form> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
The above template is referred to within the login view we just added to views.py.
Return a logged_in ag to the renderer Add the following line to the import at the head of tutorial/tutorial/views.py:
1 2 3 4 5
(Only the highlighted line needs to be added.) Add a logged_in parameter to the return value of view_page(), edit_page() and add_page(), like this:
1 2 3 4
(Only the highlighted line needs to be added.) authenticated_userid() will return None if the user is not authenticated, or some user id it the user is authenticated.
428
37.7. ADDING AUTHORIZATION Add a Logout link when logged in Open tutorial/tutorial/templates/edit.pt and tutorial/tutorial/templates/view.pt and add this within the <div id="right" class="app-welcome align-right"> div:
<span tal:condition="logged_in"> <a href="${request.application_url}/logout">Logout</a> </span>
The attribute tal:condition="logged_in" will make the element be included when logged_in is any user id. The link will invoke the logout view. The above element will not be included if logged_in is None, such as when a user is not authenticated.
from pyramid.config import Configurator from pyramid_zodbconn import get_connection from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy from .models import appmaker from .security import groupfinder def root_factory(request): conn = get_connection(request) return appmaker(conn.root())
def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ authn_policy = AuthTktAuthenticationPolicy( sosecret, callback=groupfinder, hashalg=sha512) authz_policy = ACLAuthorizationPolicy() config = Configurator(root_factory=root_factory, settings=settings) config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) config.add_static_view(static, static, cache_max_age=3600) config.scan() return config.make_wsgi_app()
429
37. ZODB + TRAVERSAL WIKI TUTORIAL (Only the highlighted lines need to be added.) Our tutorial/tutorial/models.py will look something like this when were done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
from persistent import Persistent from persistent.mapping import PersistentMapping from pyramid.security import ( Allow, Everyone, ) class Wiki(PersistentMapping): __name__ = None __parent__ = None __acl__ = [ (Allow, Everyone, view), (Allow, group:editors, edit) ] class Page(Persistent): def __init__(self, data): self.data = data def appmaker(zodb_root): if not app_root in zodb_root: app_root = Wiki() frontpage = Page(This is the front page) app_root[FrontPage] = frontpage frontpage.__name__ = FrontPage frontpage.__parent__ = app_root zodb_root[app_root] = app_root import transaction transaction.commit() return zodb_root[app_root]
(Only the highlighted lines need to be added.) Our tutorial/tutorial/views.py will look something like this when were done:
1 2 3 4 5 6 7
from docutils.core import publish_parts import re from pyramid.httpexceptions import HTTPFound from pyramid.view import ( view_config,
430
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
forbidden_view_config, ) from pyramid.security import ( remember, forget, authenticated_userid, ) from .security import USERS from .models import Page # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @view_config(context=.models.Wiki, permission=view) def view_wiki(context, request): return HTTPFound(location=request.resource_url(context, FrontPage)) @view_config(context=.models.Page, renderer=templates/view.pt, permission=view) def view_page(context, request): wiki = context.__parent__ def check(match): word = match.group(1) if word in wiki: page = wiki[word] view_url = request.resource_url(page) return <a href="%s">%s</a> % (view_url, word) else: add_url = request.application_url + /add_page/ + word return <a href="%s">%s</a> % (add_url, word) content = publish_parts(context.data, writer_name=html)[html_body] content = wikiwords.sub(check, content) edit_url = request.resource_url(context, edit_page) return dict(page = context, content = content, edit_url = edit_url, logged_in = authenticated_userid(request)) @view_config(name=add_page, context=.models.Wiki, renderer=templates/edit.pt, permission=edit) def add_page(context, request):
431
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
pagename = request.subpath[0] if form.submitted in request.params: body = request.params[body] page = Page(body) page.__name__ = pagename page.__parent__ = context context[pagename] = page return HTTPFound(location = request.resource_url(page)) save_url = request.resource_url(context, add_page, pagename) page = Page() page.__name__ = pagename page.__parent__ = context return dict(page=page, save_url=save_url, logged_in=authenticated_userid(request)) @view_config(name=edit_page, context=.models.Page, renderer=templates/edit.pt, permission=edit) def edit_page(context, request): if form.submitted in request.params: context.data = request.params[body] return HTTPFound(location = request.resource_url(context)) return dict(page=context, save_url=request.resource_url(context, edit_page), logged_in=authenticated_userid(request)) @view_config(context=.models.Wiki, name=login, renderer=templates/login.pt) @forbidden_view_config(renderer=templates/login.pt) def login(request): login_url = request.resource_url(request.context, login) referrer = request.url if referrer == login_url: referrer = / # never use the login form itself as came_from came_from = request.params.get(came_from, referrer) message = login = password = if form.submitted in request.params: login = request.params[login] password = request.params[password] if USERS.get(login) == password: headers = remember(request, login) return HTTPFound(location = came_from,
432
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
headers = headers) message = Failed login return dict( message = message, url = request.application_url + /login, came_from = came_from, login = login, password = password, ) @view_config(context=.models.Wiki, name=logout) def logout(request): headers = forget(request) return HTTPFound(location = request.resource_url(request.context), headers = headers)
(Only the highlighted lines need to be added.) Our tutorial/tutorial/templates/edit.pt template will look something like this when were done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="/static/favicon.ico" /> <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap">
433
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
<div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="/static/pyramid-small.png" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Editing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/> You can return to the <a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"> <span tal:condition="logged_in"> <a href="${request.application_url}/logout">Logout</a> </span> </div> </div> </div> <div id="bottom"> <div class="bottom"> <form action="${save_url}" method="post"> <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/> <input type="submit" name="form.submitted" value="Save"/> </form> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
(Only the highlighted lines need to be added.) Our tutorial/tutorial/templates/view.pt template will look something like this when were done:
434
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.__name__} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="/static/favicon.ico" /> <link rel="stylesheet" href="/static/pylons.css" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="/static/ie6.css" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="/static/pyramid-small.png" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Viewing <b><span tal:replace="page.__name__">Page Name Goes Here</span></b><br/> You can return to the <a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"> <span tal:condition="logged_in"> <a href="${request.application_url}/logout">Logout</a> </span> </div> </div> </div>
435
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
<div id="bottom"> <div class="bottom"> <div tal:replace="structure content"> Page text goes here. </div> <p> <a tal:attributes="href edit_url" href=""> Edit this page </a> </p> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
437
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
import unittest from pyramid import testing class PageModelTests(unittest.TestCase): def _getTargetClass(self): from .models import Page return Page def _makeOne(self, data=usome data): return self._getTargetClass()(data=data) def test_constructor(self): instance = self._makeOne() self.assertEqual(instance.data, usome data) class WikiModelTests(unittest.TestCase): def _getTargetClass(self): from .models import Wiki return Wiki def _makeOne(self): return self._getTargetClass()() def test_it(self): wiki = self._makeOne() self.assertEqual(wiki.__parent__, None) self.assertEqual(wiki.__name__, None) class AppmakerTests(unittest.TestCase): def _callFUT(self, zodb_root): from .models import appmaker return appmaker(zodb_root) def test_it(self): root = {} self._callFUT(root) self.assertEqual(root[app_root][FrontPage].data, This is the front page) class ViewWikiTests(unittest.TestCase): def test_it(self): from .views import view_wiki
438
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
context = testing.DummyResource() request = testing.DummyRequest() response = view_wiki(context, request) self.assertEqual(response.location, http://example.com/FrontPage) class ViewPageTests(unittest.TestCase): def _callFUT(self, context, request): from .views import view_page return view_page(context, request) def test_it(self): wiki = testing.DummyResource() wiki[IDoExist] = testing.DummyResource() context = testing.DummyResource(data=Hello CruelWorld IDoExist) context.__parent__ = wiki context.__name__ = thepage request = testing.DummyRequest() info = self._callFUT(context, request) self.assertEqual(info[page], context) self.assertEqual( info[content], <div class="document">\n <p>Hello <a href="http://example.com/add_page/CruelWorld"> CruelWorld</a> <a href="http://example.com/IDoExist/"> IDoExist</a> </p>\n</div>\n) self.assertEqual(info[edit_url], http://example.com/thepage/edit_page)
class AddPageTests(unittest.TestCase): def _callFUT(self, context, request): from .views import add_page return add_page(context, request) def test_it_notsubmitted(self): context = testing.DummyResource() request = testing.DummyRequest() request.subpath = [AnotherPage] info = self._callFUT(context, request) self.assertEqual(info[page].data,) self.assertEqual( info[save_url], request.resource_url(context, add_page, AnotherPage))
439
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
def test_it_submitted(self): context = testing.DummyResource() request = testing.DummyRequest({form.submitted:True, body:Hello yo!}) request.subpath = [AnotherPage] self._callFUT(context, request) page = context[AnotherPage] self.assertEqual(page.data, Hello yo!) self.assertEqual(page.__name__, AnotherPage) self.assertEqual(page.__parent__, context) class EditPageTests(unittest.TestCase): def _callFUT(self, context, request): from .views import edit_page return edit_page(context, request) def test_it_notsubmitted(self): context = testing.DummyResource() request = testing.DummyRequest() info = self._callFUT(context, request) self.assertEqual(info[page], context) self.assertEqual(info[save_url], request.resource_url(context, edit_page)) def test_it_submitted(self): context = testing.DummyResource() request = testing.DummyRequest({form.submitted:True, body:Hello yo!}) response = self._callFUT(context, request) self.assertEqual(response.location, http://example.com/) self.assertEqual(context.data, Hello yo!) class FunctionalTests(unittest.TestCase): viewer_login = /login?login=viewer&password=viewer \ &came_from=FrontPage&form.submitted=Login viewer_wrong_login = /login?login=viewer&password=incorrect \ &came_from=FrontPage&form.submitted=Login editor_login = /login?login=editor&password=editor \ &came_from=FrontPage&form.submitted=Login def setUp(self): import tempfile import os.path from . import main self.tmpdir = tempfile.mkdtemp()
440
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
dbpath = os.path.join( self.tmpdir, test.db) uri = file:// + dbpath settings = { zodbconn.uri : uri , pyramid.includes: [pyramid_zodbconn, pyramid_tm] } app = main({}, **settings) self.db = app.registry._zodb_databases[] from webtest import TestApp self.testapp = TestApp(app) def tearDown(self): import shutil self.db.close() shutil.rmtree( self.tmpdir ) def test_root(self): res = self.testapp.get(/, status=302) self.assertEqual(res.location, http://localhost/FrontPage) def test_FrontPage(self): res = self.testapp.get(/FrontPage, status=200) self.assertTrue(FrontPage in res.body) def test_unexisting_page(self): res = self.testapp.get(/SomePage, status=404) self.assertTrue(Not Found in res.body) def test_successful_log_in(self): res = self.testapp.get( self.viewer_login, status=302) self.assertEqual(res.location, http://localhost/FrontPage) def test_failed_log_in(self): res = self.testapp.get( self.viewer_wrong_login, status=200) self.assertTrue(login in res.body) def test_logout_link_present_when_logged_in(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get(/FrontPage, status=200) self.assertTrue(Logout in res.body) def test_logout_link_not_present_after_logged_out(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get(/FrontPage, status=200) res = self.testapp.get(/logout, status=302) self.assertTrue(Logout not in res.body)
441
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
def test_anonymous_user_cannot_edit(self): res = self.testapp.get(/FrontPage/edit_page, status=200) self.assertTrue(Login in res.body) def test_anonymous_user_cannot_add(self): res = self.testapp.get(/add_page/NewPage, status=200) self.assertTrue(Login in res.body) def test_viewer_user_cannot_edit(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get(/FrontPage/edit_page, status=200) self.assertTrue(Login in res.body) def test_viewer_user_cannot_add(self): res = self.testapp.get( self.viewer_login, status=302) res = self.testapp.get(/add_page/NewPage, status=200) self.assertTrue(Login in res.body) def test_editors_member_user_can_edit(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get(/FrontPage/edit_page, status=200) self.assertTrue(Editing in res.body) def test_editors_member_user_can_add(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get(/add_page/NewPage, status=200) self.assertTrue(Editing in res.body) def test_editors_member_user_can_view(self): res = self.testapp.get( self.editor_login, status=302) res = self.testapp.get(/FrontPage, status=200) self.assertTrue(FrontPage in res.body)
442
4 5 6 7 8 9 10 11
After weve added a dependency on WebTest in setup.py, we need to rerun setup.py develop to get WebTest installed into our virtualenv. Assuming our shells current working directory is the tutorial distribution directory: On UNIX:
$ ../bin/python setup.py develop
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
Once that command has completed successfully, we can run the tests themselves: On UNIX:
$ ../bin/python setup.py test -q
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
443
On Windows:
c:\pyramidtut> ..\Scripts\python setup.py sdist
Note that this command creates a tarball in the dist subdirectory named tutorial-0.1.tar.gz. You can send this le to your friends to show them your cool new application. They should be able to install it by pointing the easy_install command directly at it. Or you can upload it to PyPI and share it with the rest of the world, where it can be downloaded via easy_install remotely like any other package people download from PyPI.
444
CHAPTER
THIRTYEIGHT
38.1 Background
This tutorial presents a Pyramid application that uses technologies which will be familiar to someone with SQL database experience. It uses SQLAlchemy as a persistence mechanism and url dispatch to map URLs to code. To code along with this tutorial, the developer will need a UNIX machine with development tools (Mac OS X with XCode, any Linux or BSD variant, etc) or a Windows system of any kind.
This tutorial has been written for Python 2. It is unlikely to work without modication under Python 3.
38.2 Design
Following is a quick overview of the design of our wiki application, to help us understand the changes that we will be making as we work through the tutorial.
38.2.1 Overall
We choose to use reStructuredText markup in the wiki text. Translation from reStructuredText to HTML is provided by the widely used docutils Python module. We will add this module in the dependency list on the project setup.py le.
38.2.2 Models
Well be using a SQLite database to hold our wiki data, and well be using SQLAlchemy to access the data in this database. Within the database, we dene a single table named pages, whose elements will store the wiki pages. There are two columns: name and data. URLs like /PageName will try to nd an element in the table that has a corresponding name. To add a page to the wiki, a new row is created and the text is stored in data. A page named FrontPage containing the text This is the front page, will be created when the storage is initialized, and will be used as the wiki home page.
38.2.3 Views
There will be three views to handle the normal operations of adding, editing and viewing wiki pages, plus one view for the wiki front page. Two templates will be used, one for viewing, and one for both for adding and editing wiki pages. The default templating systems in Pyramid are Chameleon and Mako. Chameleon is a variant of ZPT , which is an XML-based templating language. Mako is a non-XML-based templating language. Because we had to pick one, we chose Chameleon for this tutorial.
446
38.2. DESIGN
38.2.4 Security
Well eventually be adding security to our application. The components well use to do this are below. USERS, a dictionary mapping users names to their corresponding passwords. GROUPS, a dictionary mapping user names to a list of groups they belong to. groupfinder, an authorization callback that looks up USERS and GROUPS. It will be provided in a new security.py le. An ACL is attached to the root resource. Each row below details an ACE: Action Allow Allow Principal Everyone group:editors Permission View Edit
Permission declarations are added to the views to assert the security policies as each request is handled. Two additional views and one template will handle the login and logout tasks.
38.2.5 Summary
The URL, actions, template and permission associated to each view are listed in the following table:
447
38. SQLALCHEMY + URL DISPATCH WIKI TUTORIAL URL / Action Redirect to /FrontPage /PageName Display existing page 1 /PageName/edit_page Display edit form with existing content. If the form was submitted, redirect to /PageName /add_page/PageName Create the page PageName in storage, display the edit form without content. If the form was submitted, redirect to /PageName /login Display login form, Forbidden 3 If the form was submitted, authenticate. If authentication successful, redirect to the page that we came from. If authentication fails, display login form with login failed message. /logout Redirect to /FrontPage View view_wiki view_page 2 edit_page Template Permission
view.pt edit.pt
view edit
add_page
edit.pt
edit
login
login.pt
logout
448
38.3. INSTALLATION
38.3 Installation
38.3.1 Preparation
Follow the steps in Installing Pyramid, but name the virtualenv directory pyramidtut.
Preparation, UNIX 1. Install SQLite3 and its development packages if you dont already have them installed. Usually this is via your systems package manager. On a Debian system, this would be:
$ sudo apt-get install libsqlite3-dev
449
On Windows:
c:\pyramidtut> Scripts\pcreate -s alchemy tutorial
If you are using Windows, the alchemy scaffold may not deal gracefully with installation into a location that contains spaces in the path. If you experience startup problems, try putting both the virtualenv and the project into directories that do not contain spaces in their paths.
On Windows:
c:\pyramidtut> cd tutorial c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
Success executing this command will end with a line to the console something like:
Finished processing dependencies for tutorial==0.0
38.3. INSTALLATION
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
For a successful test run, you should see output that ends like this:
. ---------------------------------------------------------------------Ran 1 test in 0.094s OK
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\easy_install nose coverage
Once nose and coverage are installed, we can actually run the coverage tests. On UNIX:
$ ../bin/nosetests --cover-package=tutorial --cover-erase --with-coverage
On Windows: 451
Looks like our package doesnt quite have 100% test coverage.
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\initialize_tutorial_db development.ini
452
38.3. INSTALLATION
2011-11-26 14:42:25,012 INFO 2011-11-26 14:42:25,013 INFO 2011-11-26 14:42:25,013 INFO CREATE TABLE models ( id INTEGER NOT NULL, name VARCHAR(255), value INTEGER, PRIMARY KEY (id), UNIQUE (name) ) 2011-11-26 14:42:25,013 INFO 2011-11-26 14:42:25,135 INFO 2011-11-26 14:42:25,137 INFO 2011-11-26 14:42:25,138 INFO 2011-11-26 14:42:25,139 INFO 2011-11-26 14:42:25,140 INFO
[sqlalchemy.engine.base.Engine][MainThread] () [sqlalchemy.engine.base.Engine][MainThread] COMMIT [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit) [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO models (name, value) VALUES (?, ?) [sqlalchemy.engine.base.Engine][MainThread] (uone, 1) [sqlalchemy.engine.base.Engine][MainThread] COMMIT
Success! You should now have a tutorial.sqlite le in your current working directory. This will be a SQLite database with a single table dened in it (models).
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\pserve development.ini --reload
453
Starting subprocess with file monitor Starting server in PID 8966. Starting HTTP server on http://0.0.0.0:6543
This means the server is ready to accept requests. At this point, when you visit http://localhost:6543/ in your web browser, you will see the generated applications default page. One thing youll notice is the debug toolbar icon on right hand side of the page. You can read more about the purpose of the icon at The Debug Toolbar. It allows you to get information about your application while you develop.
Pyramid supports any persistent storage mechanism (e.g. object database or lesystem les, etc). It also supports an additional mechanism to map URLs to code (traversal). However, for the purposes of this tutorial, well only be using url dispatch and 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()
Lets go over this piece-by-piece. First, we need some imports to support later code:
1 2 3 4 5 6 7
from pyramid.config import Configurator from sqlalchemy import engine_from_config from .models import ( DBSession, Base, )
__init__.py denes a function named main. Here is the entirety of the main function weve dened in our __init__.py:
455
1 2 3 4 5 6 7 8 9 10 11
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()
When you invoke the pserve development.ini command, the main function above is executed. It accepts some settings and returns a WSGI application. (See Startup for more about pserve.) The main function rst creates a SQLAlchemy database engine using engine_from_config from the sqlalchemy. prexed settings in the development.ini les [app:main] section. This will be a URI (something like sqlite://):
engine = engine_from_config(settings, sqlalchemy.)
main then initializes our SQLAlchemy session object, passing it the engine:
DBSession.configure(bind=engine)
main subsequently initializes our SQLAlchemy declarative Base object, assigning the engine we created to the bind attribute of its metadata object. This allows table denitions done imperatively (instead of declaratively, via a class statement) to work. We wont use any such tables in our application, but if you add one later, long after youve forgotten about this tutorial, you wont be left scratching your head when it doesnt work.
Base.metadata.bind = engine
settings is passed to the Congurator as a keyword argument with the dictionary values passed as the **settings argument. This will be a dictionary of settings parsed from the .ini le, which contains deployment-related values such as pyramid.reload_templates, db_string, etc. main now calls pyramid.config.Configurator.add_static_view() with two arguments: static (the name), and static (the path): 456
This registers a static resource view which will match any URL that starts with the prex /static (by virtue of the rst argument to add_static_view). This will serve up static resources for us from within the static directory of our tutorial package, in this case, via http://localhost:6543/static/ and below (by virtue of the second argument to add_static_view). With this declaration, were saying that any URL that starts with /static should go to the static view; any remainder of its path (e.g. the /foo in /static/foo) will be used to compose a path to a static le resource, such as a CSS le. Using the congurator main also registers a route conguration via the pyramid.config.Configurator.add_route() method that will be used when the URL is /:
config.add_route(home, /)
Since this route has a pattern equalling / it is the route that will be matched when the URL / is visited, e.g. http://localhost:6543/. main next calls the scan method of the congurator (pyramid.config.Configurator.scan()), which will recursively scan our tutorial package, looking for @view_config (and other special) decorators. When it nds a @view_config decorator, a view conguration will be registered, which will allow one of our application URLs to be mapped to some code.
config.scan()
Finally, main is nished conguring things, so it uses the pyramid.config.Configurator.make_wsgi_app() method to return a WSGI application:
return config.make_wsgi_app()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
from pyramid.response import Response from pyramid.view import view_config from sqlalchemy.exc import DBAPIError from .models import ( DBSession, MyModel, )
@view_config(route_name=home, renderer=templates/mytemplate.pt) def my_view(request): try: one = DBSession.query(MyModel).filter(MyModel.name == one).first() except DBAPIError: return Response(conn_err_msg, content_type=text/plain, status_int=500) return {one: one, project: tutorial} conn_err_msg = """\ Pyramid is having a problem using your SQL database. might be caused by one of the following things: 1.
The problem
You may need to run the "initialize_tutorial_db" script to initialize your database tables. Check your virtual environments "bin" directory for this script and try to run it. Your database server may not be running. Check that the database server referred to by the "sqlalchemy.url" setting in your "development.ini" file is running.
2.
After you fix the problem, please restart the Pyramid application to try it again. """
The important part here is that the @view_config decorator associates the function it decorates (my_view) with a view conguration, consisting of: a route_name (home) a renderer, which is a template from the templates subdirectory of the package. When the pattern associated with the home view is matched during a request, my_view() will be executed. my_view() returns a dictionary; the renderer will use the templates/mytemplate.pt template to create a response based on the values in the dictionary. 458
38.4. BASIC LAYOUT Note that my_view() accepts a single argument named request. This is the standard call signature for a Pyramid view callable. Remember in our __init__.py when we executed the pyramid.config.Configurator.scan() method, i.e. config.scan()? The purpose of calling the scan method was to nd and process this @view_config decorator in order to create a view conguration within our application. Without being processed by scan, the decorator effectively does nothing. @view_config is inert without being detected via a scan. The sample my_view() created by the scaffold uses a try: and except: clause, to detect if there is a problem accessing the project database and provide an alternate error response. That response will include the text shown at the end of the le, which will be displayed in the browser to inform the user about possible actions to take to solve the problem.
from sqlalchemy import ( Column, Integer, Text, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import ( scoped_session, sessionmaker, ) from zope.sqlalchemy import ZopeTransactionExtension DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) Base = declarative_base()
459
23 24 25 26 27 28
name = Column(Text, unique=True) value = Column(Integer) def __init__(self, name, value): self.name = name self.value = value
Lets examine this in detail. First, we need some imports to support later code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
from sqlalchemy import ( Column, Integer, Text, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import ( scoped_session, sessionmaker, ) from zope.sqlalchemy import ZopeTransactionExtension
scoped_session and sessionmaker are standard SQLAlchemy helpers. scoped_session allows us to access our database connection globally. sessionmaker creates a database session object. We pass to sessionmaker the extension=ZopeTransactionExtension() extension option in order to allow the system to automatically manage database transactions. With ZopeTransactionExtension activated, our application will automatically issue a transaction commit after every request unless an exception is raised, in which case the transaction will be aborted. We also need to create a declarative Base object to use as a base class for our model:
Base = declarative_base()
Our model classes will inherit from this Base class so they can be associated with our particular database connection. To give a simple example of a model class, we dene one named MyModel: 460
1 2 3 4 5 6 7 8 9
class MyModel(Base): __tablename__ = models id = Column(Integer, primary_key=True) name = Column(Text, unique=True) value = Column(Integer) def __init__(self, name, value): self.name = name self.value = value
Our example model has an __init__ method that takes a two arguments (name, and value). It stores these values as self.name and self.value within the __init__ function itself. The MyModel class also has a __tablename__ attribute. This informs SQLAlchemy which table to use to store the data representing instances of this class. Thats about all there is to it to models, views, and initialization code in our stock application.
There is nothing special about the lename models.py. A project may have many models throughout its codebase in arbitrarily-named les. Files implementing models often have model in their lenames (or they may live in a Python subpackage of your application package named models) , but this is only by convention.
461
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
from sqlalchemy import ( Column, Integer, Text, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import ( scoped_session, sessionmaker, ) from zope.sqlalchemy import ZopeTransactionExtension DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) Base = declarative_base()
class Page(Base): """ The SQLAlchemy declarative model class for a Page object. """ __tablename__ = pages id = Column(Integer, primary_key=True) name = Column(Text, unique=True) data = Column(Text) def __init__(self, name, data): self.name = name self.data = data
(The highlighted lines are the ones that need to be changed.) The rst thing weve done is to do is remove the stock MyModel class from the generated models.py le. The MyModel class is only a sample and were not going to use it. Then, we added a Page class. Because this is a SQLAlchemy application, this class inherits from an instance of sqlalchemy.ext.declarative.declarative_base.
1 2 3 4 5 6 7
class Page(Base): """ The SQLAlchemy declarative model class for a Page object. """ __tablename__ = pages id = Column(Integer, primary_key=True) name = Column(Text, unique=True) data = Column(Text)
462
8 9 10
As you can see, our Page class has a class level attribute __tablename__ which equals the string pages. This means that SQLAlchemy will store our wiki data in a SQL table named pages. Our Page class will also have class-level attributes named id, name and data (all instances of sqlalchemy.Column). These will map to columns in the pages table. The id attribute will be the primary key in the table. The name attribute will be a text attribute, each value of which needs to be unique within the column. The data attribute is a text attribute that will hold the body of each page.
import os import sys import transaction from sqlalchemy import engine_from_config from pyramid.paster import ( get_appsettings, setup_logging, ) from ..models import ( DBSession, Page, Base, )
def usage(argv):
463
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
cmd = os.path.basename(argv[0]) print(usage: %s <config_uri>\n (example: "%s development.ini") % (cmd, cmd)) sys.exit(1)
def main(argv=sys.argv): if len(argv) != 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) engine = engine_from_config(settings, sqlalchemy.) DBSession.configure(bind=engine) Base.metadata.create_all(engine) with transaction.manager: model = Page(FrontPage, This is the front page) DBSession.add(model)
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\initialize_tutorial_db development.ini
464
2011-11-27 01:22:45,277 INFO 2011-11-27 01:22:45,277 INFO 2011-11-27 01:22:45,277 INFO CREATE TABLE pages ( id INTEGER NOT NULL, name TEXT, data TEXT, PRIMARY KEY (id), UNIQUE (name) )
2011-11-27 01:22:45,278 INFO 2011-11-27 01:22:45,397 INFO 2011-11-27 01:22:45,400 INFO 2011-11-27 01:22:45,401 INFO 2011-11-27 01:22:45,401 INFO 2011-11-27 01:22:45,402 INFO
[sqlalchemy.engine.base.Engine][MainThread] () [sqlalchemy.engine.base.Engine][MainThread] COMMIT [sqlalchemy.engine.base.Engine][MainThread] BEGIN (implicit) [sqlalchemy.engine.base.Engine][MainThread] INSERT INTO pages (name, data) VALUES (?, ?) [sqlalchemy.engine.base.Engine][MainThread] (FrontPage, This is the front page) [sqlalchemy.engine.base.Engine][MainThread] COMMIT
38. SQLALCHEMY + URL DISPATCH WIKI TUTORIAL The request object passed to every view that is called as the result of a route match has an attribute named matchdict that contains the elements placed into the URL by the pattern of a route statement. For instance, if a call to pyramid.config.Configurator.add_route() in __init__.py had the pattern {one}/{two}, and the URL at http://example.com/foo/bar was invoked, matching this pattern, the matchdict dictionary attached to the request passed to the view would have a one key with the value foo and a two key with the value bar. The source code for this tutorial stage can be browsed at http://github.com/Pylons/pyramid/tree/1.3branch/docs/tutorials/wiki2/src/views/.
import os from setuptools import setup, find_packages here = os.path.abspath(os.path.dirname(__file__)) README = open(os.path.join(here, README.txt)).read() CHANGES = open(os.path.join(here, CHANGES.txt)).read() requires = [ pyramid, SQLAlchemy, transaction, pyramid_tm, pyramid_debugtoolbar, zope.sqlalchemy, waitress, docutils, ] setup(name=tutorial, version=0.0, description=tutorial,
466
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
long_description=README + \n\n + CHANGES, classifiers=[ "Programming Language :: Python", "Framework :: Pyramid", "Topic :: Internet :: WWW/HTTP", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", ], author=, author_email=, url=, keywords=web wsgi bfg pylons pyramid, packages=find_packages(), include_package_data=True, zip_safe=False, test_suite=tutorial, install_requires=requires, entry_points="""\ [paste.app_factory] main = tutorial:main [console_scripts] initialize_tutorial_db = tutorial.scripts.initializedb:main """, )
On Windows:
467
Success executing this command will end with a line to the console something like:
Finished processing dependencies for tutorial==0.0
import re from docutils.core import publish_parts from pyramid.httpexceptions import ( HTTPFound, HTTPNotFound, ) from pyramid.view import view_config from .models import ( DBSession, Page, ) # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") @view_config(route_name=view_wiki) def view_wiki(request): return HTTPFound(location = request.route_url(view_page, pagename=FrontPage)) @view_config(route_name=view_page, renderer=templates/view.pt) def view_page(request): pagename = request.matchdict[pagename] page = DBSession.query(Page).filter_by(name=pagename).first() if page is None: return HTTPNotFound(No such page) def check(match):
468
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
word = match.group(1) exists = DBSession.query(Page).filter_by(name=word).all() if exists: view_url = request.route_url(view_page, pagename=word) return <a href="%s">%s</a> % (view_url, word) else: add_url = request.route_url(add_page, pagename=word) return <a href="%s">%s</a> % (add_url, word) content = publish_parts(page.data, writer_name=html)[html_body] content = wikiwords.sub(check, content) edit_url = request.route_url(edit_page, pagename=pagename) return dict(page=page, content=content, edit_url=edit_url) @view_config(route_name=add_page, renderer=templates/edit.pt) def add_page(request): pagename = request.matchdict[pagename] if form.submitted in request.params: body = request.params[body] page = Page(pagename, body) DBSession.add(page) return HTTPFound(location = request.route_url(view_page, pagename=pagename)) save_url = request.route_url(add_page, pagename=pagename) page = Page(, ) return dict(page=page, save_url=save_url) @view_config(route_name=edit_page, renderer=templates/edit.pt) def edit_page(request): pagename = request.matchdict[pagename] page = DBSession.query(Page).filter_by(name=pagename).one() if form.submitted in request.params: page.data = request.params[body] DBSession.add(page) return HTTPFound(location = request.route_url(view_page, pagename=pagename)) return dict( page=page, save_url = request.route_url(edit_page, pagename=pagename), )
(The highlighted lines are the ones that need to be added or edited.) We got rid of the my_view view function and its decorator that was added when we originally rendered the alchemy scaffold. It was only an example and isnt relevant to our application. Then we added four view callable functions to our views.py module: 469
38. SQLALCHEMY + URL DISPATCH WIKI TUTORIAL view_wiki() - Displays the wiki itself. It will answer on the root URL. view_page() - Displays an individual page. add_page() - Allows the user to add a page. edit_page() - Allows the user to edit a page. Well describe each one briey and show the resulting views.py le afterward.
There is nothing special about the lename views.py. A project may have many view callables throughout its codebase in arbitrarily-named les. Files implementing view callables often have view in their lenames (or may live in a Python subpackage of your application package named views), but this is only by convention.
The view_wiki view function view_wiki() is the default view that gets called when a request is made to the root URL of our wiki. It always redirects to a URL which represents the path to our FrontPage.
1 2 3 4
view_wiki() returns an instance of the pyramid.httpexceptions.HTTPFound class (instances of which implement the pyramid.interfaces.IResponse interface like pyramid.response.Response does). It uses the pyramid.request.Request.route_url() API to construct a URL to the FrontPage page (e.g. http://localhost:6543/FrontPage), which is used as the location of the HTTPFound response, forming an HTTP redirect.
The view_page view function view_page() is used to display a single page of our wiki. It renders the ReStructuredText body of a page (stored as the data attribute of a Page model object) as HTML. Then it substitutes an HTML anchor for each WikiWord reference in the rendered HTML using a compiled regular expression.
470
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
@view_config(route_name=view_page, renderer=templates/view.pt) def view_page(request): pagename = request.matchdict[pagename] page = DBSession.query(Page).filter_by(name=pagename).first() if page is None: return HTTPNotFound(No such page) def check(match): word = match.group(1) exists = DBSession.query(Page).filter_by(name=word).all() if exists: view_url = request.route_url(view_page, pagename=word) return <a href="%s">%s</a> % (view_url, word) else: add_url = request.route_url(add_page, pagename=word) return <a href="%s">%s</a> % (add_url, word) content = publish_parts(page.data, writer_name=html)[html_body] content = wikiwords.sub(check, content) edit_url = request.route_url(edit_page, pagename=pagename) return dict(page=page, content=content, edit_url=edit_url)
The check() function is used as the rst argument to wikiwords.sub, indicating that it should be called to provide a value for each WikiWord match found in the content. If the wiki already contains a page with the matched WikiWord name, check() generates a view link to be used as the substitution value and returns it. If the wiki does not already contain a page with the matched WikiWord name, check() generates an add link as the substitution value and returns it. As a result, the content variable is now a fully formed bit of HTML containing various view and add links for WikiWords based on the content of our current page object. We then generate an edit URL (because its easier to do here than in the template), and we return a dictionary with a number of arguments. The fact that view_page() returns a dictionary (as opposed to a response object) is a cue to Pyramid that it should try to use a renderer associated with the view conguration to render a template. In our case, the template which will be rendered will be the templates/view.pt template, as indicated in the @view_config decorator that is applied to view_page().
The add_page view function add_page() is invoked when a user clicks on a WikiWord which isnt yet represented as a page in the system. The check function within the view_page view generates URLs to this view. add_page() 471
38. SQLALCHEMY + URL DISPATCH WIKI TUTORIAL also acts as a handler for the form that is generated when we want to add a page object. The matchdict attribute of the request passed to the add_page() view will have the values we need to construct URLs and nd model objects.
1 2 3 4 5 6 7 8 9 10 11 12
@view_config(route_name=add_page, renderer=templates/edit.pt) def add_page(request): pagename = request.matchdict[pagename] if form.submitted in request.params: body = request.params[body] page = Page(pagename, body) DBSession.add(page) return HTTPFound(location = request.route_url(view_page, pagename=pagename)) save_url = request.route_url(add_page, pagename=pagename) page = Page(, ) return dict(page=page, save_url=save_url)
The matchdict will have a pagename key that matches the name of the page wed like to add. If our add view is invoked via, e.g. http://localhost:6543/add_page/SomeName, the value for pagename in the matchdict will be SomeName. If the view execution is a result of a form submission (i.e. the expression form.submitted in request.params is True), we scrape the page body from the form data, create a Page object with this page body and the name taken from matchdict[pagename], and save it into the database using DBSession.add. We then redirect back to the view_page view for the newly created page. If the view execution is not a result of a form submission (i.e. the expression form.submitted in request.params is False), the view callable renders a template. To do so, it generates a save url which the template uses as the form post URL during rendering. Were lazy here, so were going to use the same template (templates/edit.pt) for the add view as well as the page edit view. To do so we create a dummy Page object in order to satisfy the edit forms desire to have some page object exposed as page. Pyramid will render the template associated with this view to a response.
The edit_page view function edit_page() is invoked when a user clicks the Edit this Page button on the view form. It renders an edit form but it also acts as the handler for the form it renders. The matchdict attribute of the request passed to the edit_page view will have a pagename key matching the name of the page the user wants to edit.
472
1 2 3 4 5 6 7 8 9 10 11 12 13
@view_config(route_name=edit_page, renderer=templates/edit.pt) def edit_page(request): pagename = request.matchdict[pagename] page = DBSession.query(Page).filter_by(name=pagename).one() if form.submitted in request.params: page.data = request.params[body] DBSession.add(page) return HTTPFound(location = request.route_url(view_page, pagename=pagename)) return dict( page=page, save_url = request.route_url(edit_page, pagename=pagename), )
If the view execution is a result of a form submission (i.e. the expression form.submitted in request.params is True), the view grabs the body element of the request parameters and sets it as the data attribute of the page object. It then redirects to the view_page view of the wiki page. If the view execution is not a result of a form submission (i.e. the expression form.submitted in request.params is False), the view simply renders the edit form, passing the page object and a save_url which will be used as the action of the generated form.
The view.pt Template Create tutorial/tutorial/templates/view.pt and add the following content:
1 2 3 4 5 6 7 8
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
473
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
<meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="${request.static_url(tutorial:static/favicon.ico)}" /> <link rel="stylesheet" href="${request.static_url(tutorial:static/pylons.css)}" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="${request.static_url(tutorial:static/ie6.css)}" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="${request.static_url(tutorial:static/pyramid-small.png)}" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Viewing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/> You can return to the <a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"></div> </div> </div> <div id="bottom"> <div class="bottom"> <div tal:replace="structure content"> Page text goes here. </div> <p> <a tal:attributes="href edit_url" href=""> Edit this page </a> </p> </div> </div>
474
55 56 57 58 59 60 61
</div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
This template is used by view_page() for displaying a single wiki page. It includes: A div element that is replaced with the content value provided by the view (rows 45-47). content contains HTML, so the structure keyword is used to prevent escaping it (i.e. changing > to >, etc.) A link that points at the edit URL which invokes the edit_page view for the page being viewed (rows 49-51).
The edit.pt Template Create tutorial/tutorial/templates/edit.pt and add the following content:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="${request.static_url(tutorial:static/favicon.ico)}" /> <link rel="stylesheet" href="${request.static_url(tutorial:static/pylons.css)}" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="${request.static_url(tutorial:static/ie6.css)}" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body>
475
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
<div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="${request.static_url(tutorial:static/pyramid-small.png)}" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Editing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/> You can return to the <a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"></div> </div> </div> <div id="bottom"> <div class="bottom"> <form action="${save_url}" method="post"> <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/> <input type="submit" name="form.submitted" value="Save"/> </form> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
This template is used by add_page() and edit_page() for adding and editing a wiki page. It displays a page containing a form that includes: A 10 row by 60 column textarea eld named body that is lled with any existing page data when it is rendered (rows 46-47). A submit button that has the name form.submitted (row 48).
476
38.6. DEFINING VIEWS The form POSTs back to the save_url argument supplied by the view (row 45). The view will use the body and form.submitted values.
Our templates use a request object that none of our tutorial views return in their dictionary. request is one of several names that are available by default in a template when a template renderer is used. See *.pt or *.txt: Chameleon Template Renderers for information about other names that are available by default when a Chameleon template is used as a renderer.
Static Assets Our templates name a single static asset named pylons.css. We dont need to create this le within our packages static directory because it was provided at the time we created the project. This le is a little too long to replicate within the body of this guide, however it is available online. This CSS le will be accessed via e.g. http://localhost:6543/static/pylons.css by virtue of the call to add_static_view directive weve made in the __init__.py le. Any number and type of static assets can be placed in this directory (or subdirectories) and are just referred to by URL or by using the convenience method static_url e.g. request.static_url({{package}}:static/foo.css) within templates.
477
38. SQLALCHEMY + URL DISPATCH WIKI TUTORIAL 3. Add a declaration which maps the pattern /add_page/{pagename} to the route named add_page. This is the add view for a new page. It maps to our add_page view callable by virtue of the @view_config attached to the add_page view function indicating route_name=add_page. 4. Add a declaration which maps the pattern /{pagename}/edit_page to the route named edit_page. This is the edit view for a page. It maps to our edit_page view callable by virtue of the @view_config attached to the edit_page view function indicating route_name=edit_page. As a result of our edits, the __init__.py le should look something like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
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(view_wiki, /) config.add_route(view_page, /{pagename}) config.add_route(add_page, /add_page/{pagename}) config.add_route(edit_page, /{pagename}/edit_page) config.scan() return config.make_wsgi_app()
(The highlighted lines are the ones that need to be added or edited.)
38.7. ADDING AUTHORIZATION http://localhost:6543 in a browser invokes the view_wiki view. This always redirects to the view_page view of the FrontPage page object. http://localhost:6543/FrontPage in a browser invokes the view_page view of the front page object. http://localhost:6543/FrontPage/edit_page in a browser invokes the edit view for the front page object. http://localhost:6543/add_page/SomePageName in a browser invokes the add view for a page. To generate an error, visit http://localhost:6543/foobars/edit_page which will generate a NoResultFound: No row was found for one() error. Youll see an interactive traceback facility provided by pyramid_debugtoolbar.
USERS = {editor:editor, viewer:viewer} GROUPS = {editor:[group:editors]} def groupfinder(userid, request): if userid in USERS: return GROUPS.get(userid, [])
The groupfinder function accepts a userid and a request and returns one of these values: If the userid exists in the system, it will return a sequence of group identiers (or an empty sequence if the user isnt a member of any groups). If the userid does not exist in the system, it will return None. For example, groupfinder(editor, request ) returns [group:editor], groupfinder(viewer, request) returns [], and groupfinder(admin, request) returns None. We will use groupfinder() as an authentication policy callback that will provide the principal or principals for a user. In a production system, user and group data will most often come from a database, but here we use dummy data to represent user and groups sources.
Add an ACL Open tutorial/tutorial/models.py and add the following import statement at the head:
1 2 3 4
480
1 2 3 4 5
class RootFactory(object): __acl__ = [ (Allow, Everyone, view), (Allow, group:editors, edit) ] def __init__(self, request): pass
We import Allow, an action that means that permission is allowed:, and Everyone, a special principal that is associated to all requests. Both are used in the ACE entries that make up the ACL. The ACL is a list that needs to be named __acl__ and be an attribute of a class. We dene an ACL with two ACE entries: the rst entry allows any user the view permission. The second entry allows the group:editors principal the edit permission. The RootFactory class that contains the ACL is a root factory. We need to associate it to our Pyramid application, so the ACL is provided to each view in the context of the request, as the context attribute. Open tutorial/tutorial/__init__.py and add a root_factory parameter to our Congurator constructor, that points to the class we created above:
1 2
(Only the highlighted line needs to be added.) We are now providing the ACL to the application. See Assigning ACLs to your Resource Objects for more information about what an ACL represents. Although we dont use the functionality here, the factory used to create route contexts may differ per-route as opposed to globally. See the factory argument to pyramid.config.Configurator.add_route() for more info.
Add Authentication and Authorization Policies Open tutorial/__init__.py and add these import statements:
1 2 3
from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy from tutorial.security import groupfinder
1 2 3 4 5 6 7
authn_policy = AuthTktAuthenticationPolicy( sosecret, callback=groupfinder, hashalg=sha512) authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings, root_factory=tutorial.models.RootFactory) config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy)
(Only the highlighted lines need to be added.) We are enabling an AuthTktAuthenticationPolicy, it is based in an auth ticket that may be included in the request, and an ACLAuthorizationPolicy that uses an ACL to determine the allow or deny outcome for a view. Note that the pyramid.authentication.AuthTktAuthenticationPolicy constructor accepts two arguments: secret and callback. secret is a string representing an encryption key used by the authentication ticket machinery represented by this policy: it is required. The callback is the groupfinder() function that we created before. Add permission declarations Add a permission=edit parameter to the @view_config decorator for add_page() and edit_page(), for example:
1 2
(Only the highlighted line needs to be added.) The result is that only users who possess the edit permission at the time of the request may invoke those two views. Add a permission=view parameter to the @view_config decorator for view_wiki() and view_page(), like this:
1 2
(Only the highlighted line needs to be added.) This allows anyone to invoke these two views. We are done with the changes needed to control access. The changes that follow will add the login and logout feature. 482
Add Login and Logout Views Well add a login view which renders a login form and processes the post from the login form, checking credentials. Well also add a logout view callable to our application and provide a link to it. This view will clear the credentials of the logged in user and redirect back to the front page. Add the following import statements to the head of tutorial/tutorial/views.py:
1 2 3 4 5 6 7 8 9 10 11
from pyramid.view import ( view_config, forbidden_view_config, ) from pyramid.security import ( remember, forget, ) from .security import USERS
(Only the highlighted lines need to be added.) forbidden_view_config() will be used to customize the default 403 Forbidden page. remember() and forget() help to create and expire an auth ticket cookie. Now add the login and logout views:
483
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
@view_config(route_name=login, renderer=templates/login.pt) @forbidden_view_config(renderer=templates/login.pt) def login(request): login_url = request.route_url(login) referrer = request.url if referrer == login_url: referrer = / # never use the login form itself as came_from came_from = request.params.get(came_from, referrer) message = login = password = if form.submitted in request.params: login = request.params[login] password = request.params[password] if USERS.get(login) == password: headers = remember(request, login) return HTTPFound(location = came_from, headers = headers) message = Failed login return dict( message = message, url = request.application_url + /login, came_from = came_from, login = login, password = password, ) @view_config(route_name=logout) def logout(request): headers = forget(request) return HTTPFound(location = request.route_url(view_wiki), headers = headers)
login() is decorated with two decorators: a @view_config decorator which associates it with the login route and makes it visible when we visit /login, a @forbidden_view_config decorator which turns it into an forbidden view. login() will be invoked when a users tries to execute a view callable that they are not allowed to. For example, if a user has not logged in and tries to add or edit a Wiki page, he will be shown the login form before being allowed to continue on. The order of these two view conguration decorators is unimportant. logout() is decorated with a @view_config decorator which associates it with the logout route. It will be invoked when we visit /logout. 484
38.7. ADDING AUTHORIZATION Add the login.pt Template Create tutorial/tutorial/templates/login.pt with the following content:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="${request.static_url(tutorial:static/favicon.ico)}" /> <link rel="stylesheet" href="${request.static_url(tutorial:static/pylons.css)}" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="${request.static_url(tutorial:static/ie6.css)}" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="${request.static_url(tutorial:static/pyramid-small.png)}" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> <b>Login</b><br/> <span tal:replace="message"/> </div> <div id="right" class="app-welcome align-right"></div> </div> </div> <div id="bottom"> <div class="bottom">
485
<form action="${url}" method="post"> <input type="hidden" name="came_from" value="${came_from}"/> <input type="text" name="login" value="${login}"/><br/> <input type="password" name="password" value="${password}"/><br/> <input type="submit" name="form.submitted" value="Log In"/> </form> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
The above template is referred to within the login view we just added to views.py.
Return a logged_in ag to the renderer Add the following line to the import at the head of tutorial/tutorial/views.py:
1 2 3 4 5
(Only the highlighted line needs to be added.) Add a logged_in parameter to the return value of view_page(), edit_page() and add_page(), like this:
1 2 3 4
(Only the highlighted line needs to be added.) authenticated_userid() will return None if the user is not authenticated, or some user id it the user is authenticated. 486
38.7. ADDING AUTHORIZATION Add a Logout link when logged in Open tutorial/tutorial/templates/edit.pt and tutorial/tutorial/templates/view.pt and add this within the <div id="right" class="app-welcome align-right"> div:
<span tal:condition="logged_in"> <a href="${request.application_url}/logout">Logout</a> </span>
The attribute tal:condition="logged_in" will make the element be included when logged_in is any user id. The link will invoke the logout view. The above element will not be included if logged_in is None, such as when a user is not authenticated.
from pyramid.config import Configurator from pyramid.authentication import AuthTktAuthenticationPolicy from pyramid.authorization import ACLAuthorizationPolicy from sqlalchemy import engine_from_config from tutorial.security import groupfinder 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 authn_policy = AuthTktAuthenticationPolicy( sosecret, callback=groupfinder, hashalg=sha512) authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings, root_factory=tutorial.models.RootFactory)
487
26 27 28 29 30 31 32 33 34 35 36
config.set_authentication_policy(authn_policy) config.set_authorization_policy(authz_policy) config.add_static_view(static, static, cache_max_age=3600) config.add_route(view_wiki, /) config.add_route(login, /login) config.add_route(logout, /logout) config.add_route(view_page, /{pagename}) config.add_route(add_page, /add_page/{pagename}) config.add_route(edit_page, /{pagename}/edit_page) config.scan() return config.make_wsgi_app()
(Only the highlighted lines need to be added.) Our tutorial/tutorial/models.py will look something like this when were done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
from pyramid.security import ( Allow, Everyone, ) from sqlalchemy import ( Column, Integer, Text, ) from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import ( scoped_session, sessionmaker, ) from zope.sqlalchemy import ZopeTransactionExtension DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension())) Base = declarative_base()
class Page(Base): """ The SQLAlchemy declarative model class for a Page object. """ __tablename__ = pages id = Column(Integer, primary_key=True) name = Column(Text, unique=True)
488
30 31 32 33 34 35 36 37 38 39 40
data = Column(Text) def __init__(self, name, data): self.name = name self.data = data class RootFactory(object): __acl__ = [ (Allow, Everyone, view), (Allow, group:editors, edit) ] def __init__(self, request): pass
(Only the highlighted lines need to be added.) Our tutorial/tutorial/views.py will look something like this when were done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
import re from docutils.core import publish_parts from pyramid.httpexceptions import ( HTTPFound, HTTPNotFound, ) from pyramid.view import ( view_config, forbidden_view_config, ) from pyramid.security import ( remember, forget, authenticated_userid, ) from .models import ( DBSession, Page, ) from .security import USERS # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
489
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
@view_config(route_name=view_wiki, permission=view) def view_wiki(request): return HTTPFound(location = request.route_url(view_page, pagename=FrontPage)) @view_config(route_name=view_page, renderer=templates/view.pt, permission=view) def view_page(request): pagename = request.matchdict[pagename] page = DBSession.query(Page).filter_by(name=pagename).first() if page is None: return HTTPNotFound(No such page) def check(match): word = match.group(1) exists = DBSession.query(Page).filter_by(name=word).all() if exists: view_url = request.route_url(view_page, pagename=word) return <a href="%s">%s</a> % (view_url, word) else: add_url = request.route_url(add_page, pagename=word) return <a href="%s">%s</a> % (add_url, word) content = publish_parts(page.data, writer_name=html)[html_body] content = wikiwords.sub(check, content) edit_url = request.route_url(edit_page, pagename=pagename) return dict(page=page, content=content, edit_url=edit_url, logged_in=authenticated_userid(request)) @view_config(route_name=add_page, renderer=templates/edit.pt, permission=edit) def add_page(request): pagename = request.matchdict[pagename] if form.submitted in request.params: body = request.params[body] page = Page(pagename, body) DBSession.add(page) return HTTPFound(location = request.route_url(view_page, pagename=pagename)) save_url = request.route_url(add_page, pagename=pagename) page = Page(, ) return dict(page=page, save_url=save_url, logged_in=authenticated_userid(request)) @view_config(route_name=edit_page, renderer=templates/edit.pt,
490
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
permission=edit) def edit_page(request): pagename = request.matchdict[pagename] page = DBSession.query(Page).filter_by(name=pagename).one() if form.submitted in request.params: page.data = request.params[body] DBSession.add(page) return HTTPFound(location = request.route_url(view_page, pagename=pagename)) return dict( page=page, save_url = request.route_url(edit_page, pagename=pagename), logged_in=authenticated_userid(request), ) @view_config(route_name=login, renderer=templates/login.pt) @forbidden_view_config(renderer=templates/login.pt) def login(request): login_url = request.route_url(login) referrer = request.url if referrer == login_url: referrer = / # never use the login form itself as came_from came_from = request.params.get(came_from, referrer) message = login = password = if form.submitted in request.params: login = request.params[login] password = request.params[password] if USERS.get(login) == password: headers = remember(request, login) return HTTPFound(location = came_from, headers = headers) message = Failed login return dict( message = message, url = request.application_url + /login, came_from = came_from, login = login, password = password, ) @view_config(route_name=logout) def logout(request): headers = forget(request)
491
122 123
(Only the highlighted lines need to be added.) Our tutorial/tutorial/templates/edit.pt template will look something like this when were done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="${request.static_url(tutorial:static/favicon.ico)}" /> <link rel="stylesheet" href="${request.static_url(tutorial:static/pylons.css)}" type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="${request.static_url(tutorial:static/ie6.css)}" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="${request.static_url(tutorial:static/pyramid-small.png)}" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Editing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/> You can return to the
492
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
<a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"> <span tal:condition="logged_in"> <a href="${request.application_url}/logout">Logout</a> </span> </div> </div> </div> <div id="bottom"> <div class="bottom"> <form action="${save_url}" method="post"> <textarea name="body" tal:content="page.data" rows="10" cols="60"/><br/> <input type="submit" name="form.submitted" value="Save"/> </form> </div> </div> </div> <div id="footer"> <div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
(Only the highlighted lines need to be added.) Our tutorial/tutorial/templates/view.pt template will look something like this when were done:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" xmlns:tal="http://xml.zope.org/namespaces/tal"> <head> <title>${page.name} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/> <meta name="keywords" content="python web application" /> <meta name="description" content="pyramid web application" /> <link rel="shortcut icon" href="${request.static_url(tutorial:static/favicon.ico)}" /> <link rel="stylesheet" href="${request.static_url(tutorial:static/pylons.css)}"
493
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
type="text/css" media="screen" charset="utf-8" /> <!--[if lte IE 6]> <link rel="stylesheet" href="${request.static_url(tutorial:static/ie6.css)}" type="text/css" media="screen" charset="utf-8" /> <![endif]--> </head> <body> <div id="wrap"> <div id="top-small"> <div class="top-small align-center"> <div> <img width="220" height="50" alt="pyramid" src="${request.static_url(tutorial:static/pyramid-small.png)}" /> </div> </div> </div> <div id="middle"> <div class="middle align-right"> <div id="left" class="app-welcome align-left"> Viewing <b><span tal:replace="page.name">Page Name Goes Here</span></b><br/> You can return to the <a href="${request.application_url}">FrontPage</a>.<br/> </div> <div id="right" class="app-welcome align-right"> <span tal:condition="logged_in"> <a href="${request.application_url}/logout">Logout</a> </span> </div> </div> </div> <div id="bottom"> <div class="bottom"> <div tal:replace="structure content"> Page text goes here. </div> <p> <a tal:attributes="href edit_url" href=""> Edit this page </a> </p> </div> </div> </div> <div id="footer">
494
61 62 63 64 65
<div class="footer" >© Copyright 2008-2011, Agendaless Consulting.</div> </div> </body> </html>
def _initTestingDB(): from sqlalchemy import create_engine from tutorial.models import ( DBSession, Page, Base ) engine = create_engine(sqlite://) Base.metadata.create_all(engine) DBSession.configure(bind=engine)
496
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
with transaction.manager: model = Page(FrontPage, This is the front page) DBSession.add(model) return DBSession
class PageModelTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() def tearDown(self): self.session.remove() def _getTargetClass(self): from tutorial.models import Page return Page def _makeOne(self, name=SomeName, data=some data): return self._getTargetClass()(name, data) def test_constructor(self): instance = self._makeOne() self.assertEqual(instance.name, SomeName) self.assertEqual(instance.data, some data)
class ViewWikiTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() def tearDown(self): testing.tearDown() def _callFUT(self, request): from tutorial.views import view_wiki return view_wiki(request) def test_it(self): _registerRoutes(self.config)
497
class ViewPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() self.config = testing.setUp() def tearDown(self): self.session.remove() testing.tearDown() def _callFUT(self, request): from tutorial.views import view_page return view_page(request) def test_it(self): from tutorial.models import Page request = testing.DummyRequest() request.matchdict[pagename] = IDoExist page = Page(IDoExist, Hello CruelWorld IDoExist) self.session.add(page) _registerRoutes(self.config) info = self._callFUT(request) self.assertEqual(info[page], page) self.assertEqual( info[content], <div class="document">\n <p>Hello <a href="http://example.com/add_page/CruelWorld"> CruelWorld</a> <a href="http://example.com/IDoExist"> IDoExist</a> </p>\n</div>\n) self.assertEqual(info[edit_url], http://example.com/IDoExist/edit_page)
class AddPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() self.config = testing.setUp() def tearDown(self): self.session.remove()
498
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
testing.tearDown() def _callFUT(self, request): from tutorial.views import add_page return add_page(request) def test_it_notsubmitted(self): _registerRoutes(self.config) request = testing.DummyRequest() request.matchdict = {pagename:AnotherPage} info = self._callFUT(request) self.assertEqual(info[page].data,) self.assertEqual(info[save_url], http://example.com/add_page/AnotherPage) def test_it_submitted(self): from tutorial.models import Page _registerRoutes(self.config) request = testing.DummyRequest({form.submitted:True, body:Hello yo!}) request.matchdict = {pagename:AnotherPage} self._callFUT(request) page = self.session.query(Page).filter_by(name=AnotherPage).one() self.assertEqual(page.data, Hello yo!)
class EditPageTests(unittest.TestCase): def setUp(self): self.session = _initTestingDB() self.config = testing.setUp() def tearDown(self): self.session.remove() testing.tearDown() def _callFUT(self, request): from tutorial.views import edit_page return edit_page(request) def test_it_notsubmitted(self): from tutorial.models import Page _registerRoutes(self.config) request = testing.DummyRequest() request.matchdict = {pagename:abc} page = Page(abc, hello) self.session.add(page)
499
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
info = self._callFUT(request) self.assertEqual(info[page], page) self.assertEqual(info[save_url], http://example.com/abc/edit_page) def test_it_submitted(self): from tutorial.models import Page _registerRoutes(self.config) request = testing.DummyRequest({form.submitted:True, body:Hello yo!}) request.matchdict = {pagename:abc} page = Page(abc, hello) self.session.add(page) response = self._callFUT(request) self.assertEqual(response.location, http://example.com/abc) self.assertEqual(page.data, Hello yo!)
class FunctionalTests(unittest.TestCase): viewer_login = /login?login=viewer&password=viewer \ &came_from=FrontPage&form.submitted=Login viewer_wrong_login = /login?login=viewer&password=incorrect \ &came_from=FrontPage&form.submitted=Login editor_login = /login?login=editor&password=editor \ &came_from=FrontPage&form.submitted=Login def setUp(self): from tutorial import main settings = { sqlalchemy.url: sqlite://} app = main({}, **settings) from webtest import TestApp self.testapp = TestApp(app) _initTestingDB() def tearDown(self): del self.testapp from tutorial.models import DBSession DBSession.remove() def test_root(self): res = self.testapp.get(/, status=302) self.assertEqual(res.location, http://localhost/FrontPage) def test_FrontPage(self): res = self.testapp.get(/FrontPage, status=200)
500
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
self.assertTrue(bFrontPage in res.body) def test_unexisting_page(self): self.testapp.get(/SomePage, status=404) def test_successful_log_in(self): res = self.testapp.get(self.viewer_login, status=302) self.assertEqual(res.location, http://localhost/FrontPage) def test_failed_log_in(self): res = self.testapp.get(self.viewer_wrong_login, status=200) self.assertTrue(blogin in res.body) def test_logout_link_present_when_logged_in(self): self.testapp.get(self.viewer_login, status=302) res = self.testapp.get(/FrontPage, status=200) self.assertTrue(bLogout in res.body) def test_logout_link_not_present_after_logged_out(self): self.testapp.get(self.viewer_login, status=302) self.testapp.get(/FrontPage, status=200) res = self.testapp.get(/logout, status=302) self.assertTrue(bLogout not in res.body) def test_anonymous_user_cannot_edit(self): res = self.testapp.get(/FrontPage/edit_page, status=200) self.assertTrue(bLogin in res.body) def test_anonymous_user_cannot_add(self): res = self.testapp.get(/add_page/NewPage, status=200) self.assertTrue(bLogin in res.body) def test_viewer_user_cannot_edit(self): self.testapp.get(self.viewer_login, status=302) res = self.testapp.get(/FrontPage/edit_page, status=200) self.assertTrue(bLogin in res.body) def test_viewer_user_cannot_add(self): self.testapp.get(self.viewer_login, status=302) res = self.testapp.get(/add_page/NewPage, status=200) self.assertTrue(bLogin in res.body) def test_editors_member_user_can_edit(self): self.testapp.get(self.editor_login, status=302) res = self.testapp.get(/FrontPage/edit_page, status=200) self.assertTrue(bEditing in res.body)
501
247 248 249 250 251 252 253 254 255 256
def test_editors_member_user_can_add(self): self.testapp.get(self.editor_login, status=302) res = self.testapp.get(/add_page/NewPage, status=200) self.assertTrue(bEditing in res.body) def test_editors_member_user_can_view(self): self.testapp.get(self.editor_login, status=302) res = self.testapp.get(/FrontPage, status=200) self.assertTrue(bFrontPage in res.body)
requires = [ pyramid, SQLAlchemy, transaction, pyramid_tm, pyramid_debugtoolbar, zope.sqlalchemy, waitress, docutils, WebTest, # add this ]
After weve added a dependency on WebTest in setup.py, we need to rerun setup.py develop to get WebTest installed into our virtualenv. Assuming our shells current working directory is the tutorial distribution directory: On UNIX:
$ ../bin/python setup.py develop
On Windows:
502
Once that command has completed successfully, we can run the tests themselves: On UNIX:
$ ../bin/python setup.py test -q
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
On Windows:
c:\pyramidtut> ..\Scripts\python setup.py sdist
503
running sdist # ... more output ... creating dist tar -cf dist/tutorial-0.0.tar tutorial-0.0 gzip -f9 dist/tutorial-0.0.tar removing tutorial-0.0 (and everything under it)
Note that this command creates a tarball in the dist subdirectory named tutorial-0.0.tar.gz. You can send this le to your friends to show them your cool new application. They should be able to install it by pointing the easy_install command directly at it. Or you can upload it to PyPI and share it with the rest of the world, where it can be downloaded via easy_install remotely like any other package people download from PyPI.
504
CHAPTER
THIRTYNINE
505
bfgenv above will be the virtualenv into which youve installed repoze.bfg 1.3. 2. Install Pyramid into a separate virtualenv as per the instructions in Installing Pyramid. The Pyramid virtualenv should be separate from the one youve used to install repoze.bfg. A quick way to do this:
$ $ $ $ cd ~ virtualenv --no-site-packages pyramidenv cd pyramidenv bin/easy_install pyramid
3. Put a copy of your repoze.bfg application into a temporary location (perhaps by checking a fresh copy of the application out of a version control repository). For example:
$ cd /tmp $ svn co http://my.server/my/bfg/application/trunk bfgapp
4. Use the bfg2pyramid script present in the bin directory of the Pyramid virtualenv to convert all repoze.bfg Python import statements into compatible Pyramid import statements. bfg2pyramid will also x ZCML directive usages of common repoze.bfg directives. You invoke bfg2pyramid by passing it the path of the copy of your application. The path passed should contain a setup.py le, representing your repoze.bfg applications setup script. bfg2pyramid will change the copy of the application in place.
$ ~/pyramidenv/bfg2pyramid /tmp/bfgapp
bfg2pyramid will convert the following repoze.bfg application aspects to Pyramid compatible analogues: Python import statements naming repoze.bfg APIs will be converted to Pyramid compatible import statements. Every Python le beneath the top-level path will be visited and converted recursively, except Python les which live in directories which start with a . (dot). Each ZCML le found (recursively) within the path will have the default xmlns attribute attached to the configure tag changed from http://namespaces.repoze.org/bfg to http://pylonshq.com/pyramid. Every ZCML le beneath the top-level path (les ending with .zcml) will be visited and converted recursively, except ZCML les which live in directories which start with a . (dot). 506
ZCML les which contain directives that have attributes which name a repoze.bfg API module or attribute of an API module (e.g. context="repoze.bfg.exceptions.NotFound") will be converted to Pyramid compatible ZCML attributes (e.g. context="pyramid.exceptions.NotFound). Every ZCML le beneath the top-level path (les ending with .zcml) will be visited and converted recursively, except ZCML les which live in directories which start with a . (dot). 5. Edit the setup.py le of the application youve just converted (if youve been using the example paths, this will be /tmp/bfgapp/setup.py) to depend on the pyramid distribution instead the of repoze.bfg distribution in its install_requires list. If you used a scaffold to create the repoze.bfg application, you can do so by changing the requires line near the top of the setup.py le. The original may look like this:
requires = [repoze.bfg, ... other dependencies ...]
All other install-requires and tests-requires dependencies save for the one on repoze.bfg can remain the same. 6. Convert any install_requires dependencies your application has on other add-on packages which have repoze.bfg in their names to Pyramid compatible analogues (e.g. repoze.bfg.jinja2 should be replaced with pyramid_jinja2). You may need to adjust conguration options and/or imports in your repoze.bfg application after replacing these add-ons. Read the documentation of the Pyramid add-on package for information. 7. Only if you use ZCML and add-ons which use ZCML: The default xmlns of the configure tag in ZCML has changed. The bfg2pyramid script effects the default namespace change (it changes the configure tag default xmlns from http://namespaces.repoze.org/bfg to http://pylonshq.com/pyramid). This means that uses of add-ons which dene ZCML directives in the http://namespaces.repoze.org/bfg namespace will begin to fail (theyre actually not really failing, but your ZCML assumes that they will always be used within a configure tag which names the http://namespaces.repoze.org/bfg namespace as its default xmlns). Symptom: when you attempt to start the application, an error such as ConfigurationError: (Unknown directive, uhttp://namespaces.repoze.org/bfg, uworkflow) is printed to the console and the application fails to start. In such a case, either add an xmlns="http://namespaces.repoze.org/bfg" attribute to each tag which causes a failure, or dene a namespace alias in the congure tag and prex each failing tag. For example, change this failing tag instance: 507
You will also need to add the pyramid_zcml package to your setup.py install_requires list. In Pyramid, ZCML conguration became an optional add-on supported by the pyramid_zcml package. 8. Retest your application using Pyramid. This might be as easy as:
$ cd /tmp/bfgapp $ ~/pyramidenv/bin/python setup.py test
9. Fix any test failures. 10. Fix any code which generates deprecation warnings. 11. Start using the converted version of your application. Celebrate. Two terminological changes have been made to Pyramid which make its documentation and newer APIs different than those of repoze.bfg. The concept that BFG called model is called resource in Pyramid and the concept that BFG called resource is called asset in Pyramid. Various APIs have changed as a result (although all have backwards compatible shims). Additionally, the environment variables that inuenced server behavior which used to be prexed with BFG_ (such as BFG_DEBUG_NOTFOUND) must now be prexed with PYRAMID_.
508
CHAPTER
FORTY
This command may need to be performed as the root user. 4. Create a virtualenv which well use to install our application. 509
$ $ $ $
6. Create and install your Pyramid application. For the purposes of this tutorial, well just be using the pyramid_starter application as a baseline application. Substitute your existing Pyramid application as necessary if you already have one.
$ $ $ $ cd ~/modwsgi/env bin/pcreate -s starter myapp cd myapp ../bin/python setup.py install
7. Within the virtualenv directory (~/modwsgi/env), create a script named pyramid.wsgi. Give it these contents:
from pyramid.paster import get_app, setup_logging ini_path = /Users/chrism/modwsgi/env/myapp/production.ini setup_logging(ini_path) application = get_app(ini_path, main)
The rst argument to get_app is the project conguration le name. Its best to use the production.ini le provided by your scaffold, as it contains settings appropriate for production. The second is the name of the section within the .ini le that should be loaded by mod_wsgi. The assignment to the name application is important: mod_wsgi requires nding such an assignment when it opens the le. The call to setup_logging initializes the standard librarys logging module to allow logging within your application. See Logging Conguration. There is no need to make the pyramid.wsgi script executable. However, youll need to make sure that two users have access to change into the ~/modwsgi/env directory: your current user (mine is chrism and the user that Apache will run as often named apache or httpd). Make sure both of these users can cd into that directory. 510
8. Edit your Apache conguration and add some stuff. I happened to create a le named /etc/apache2/other/modwsgi.conf on my own system while installing Apache, so this stuff went in there.
# Use only 1 Python sub-interpreter. Multiple sub-interpreters # play badly with C extensions. See # http://stackoverflow.com/a/10558360/209039 WSGIApplicationGroup %{GLOBAL} WSGIPassAuthorization On WSGIDaemonProcess pyramid user=chrism group=staff threads=4 \ python-path=/Users/chrism/modwsgi/env/lib/python2.6/site-packages WSGIScriptAlias /myapp /Users/chrism/modwsgi/env/pyramid.wsgi <Directory /Users/chrism/modwsgi/env> WSGIProcessGroup pyramid Order allow,deny Allow from all </Directory>
9. Restart Apache
$ sudo /usr/sbin/apachectl restart
10. Visit http://localhost/myapp in a browser. You should see the sample application rendered in your browser. mod_wsgi has many knobs and a great variety of deployment modes. This is just one representation of how you might use it to serve up a Pyramid application. See the mod_wsgi conguration documentation for more in-depth conguration information.
511
512
Part III
API Reference
CHAPTER
FORTYONE
PYRAMID.AUTHORIZATION
class ACLAuthorizationPolicy An authorization policy which consults an ACL object attached to a context to determine authorization information about a principal or multiple principals. If the context is part of a lineage, the contexts parents are consulted for ACL information too. The following is true about this security policy. When checking whether the current user is permitted (via the permits method), the security policy consults the context for an ACL rst. If no ACL exists on the context, or one does exist but the ACL does not explicitly allow or deny access for any of the effective principals, consult the contexts parent ACL, and so on, until the lineage is exhausted or we determine that the policy permits or denies. During this processing, if any pyramid.security.Deny ACE is found matching any principal in principals, stop processing by returning an pyramid.security.ACLDenied instance (equals False) immediately. If any pyramid.security.Allow ACE is found matching any principal, stop processing by returning an pyramid.security.ACLAllowed instance (equals True) immediately. If we exhaust the contexts lineage, and no ACE has explicitly permitted or denied access, return an instance of pyramid.security.ACLDenied (equals False). When computing principals allowed by a permission via the pyramid.security.principals_allowed_by_permission() method, we compute the set of principals that are explicitly granted the permission in the provided context. We do this by walking up the object graph from the root to the context. During this walking process, if we nd an explicit pyramid.security.Allow ACE for a principal that matches the permission, the principal is included in the allow list. However, if later in the walking process that principal is mentioned 515
41. PYRAMID.AUTHORIZATION in any pyramid.security.Deny ACE for the permission, the principal is removed from the allow list. If a pyramid.security.Deny to the principal pyramid.security.Everyone is encountered during the walking process that matches the permission, the allow list is cleared for all principals encountered in previous ACLs. The walking process ends after weve processed the any ACL directly attached to context; a set of principals is returned. Objects of this class implement the pyramid.interfaces.IAuthorizationPolicy interface.
516
CHAPTER
FORTYTWO
PYRAMID.AUTHENTICATION
42.1 Authentication Policies
class AuthTktAuthenticationPolicy(secret, callback=None, cookie_name=auth_tkt, secure=False, include_ip=False, timeout=None, reissue_time=None, max_age=None, path=/, http_only=False, wild_domain=True, debug=False, hashalg=<object object at 0x3493d60>) A Pyramid authentication policy which obtains data from a Pyramid auth ticket cookie.
The default hash algorithm used in this policy is MD5 and has known hash collision vulnerabilities. The risk of an exploit is low. However, for improved authentication security, use hashalg=sha512. Constructor Arguments secret The secret (a string) used for auth_tkt cookie signing. Required. callback Default: None. A callback passed the userid and the request, expected to return None if the userid doesnt exist or a sequence of principal identiers (possibly empty) if the user does exist. If callback is None, the userid will be assumed to exist with no principals. Optional. 517
42. PYRAMID.AUTHENTICATION cookie_name Default: auth_tkt. The cookie name used (string). Optional. secure Default: False. Only send the cookie back over a secure conn. Optional. include_ip Default: False. Make the requesting IP address part of the authentication data in the cookie. Optional. timeout Default: None. Maximum number of seconds which a newly issued ticket will be considered valid. After this amount of time, the ticket will expire (effectively logging the user out). If this value is None, the ticket never expires. Optional. reissue_time Default: None. If this parameter is set, it represents the number of seconds that must pass before an authentication token cookie is automatically reissued as the result of a request which requires authentication. The duration is measured as the number of seconds since the last auth_tkt cookie was issued and now. If this value is 0, a new ticket cookie will be reissued on every request which requires authentication. A good rule of thumb: if you want auto-expired cookies based on inactivity: set the timeout value to 1200 (20 mins) and set the reissue_time value to perhaps a tenth of the timeout value (120 or 2 mins). Its nonsensical to set the timeout value lower than the reissue_time value, as the ticket will never be reissued if so. However, such a conguration is not explicitly prevented. Optional. max_age Default: None. The max age of the auth_tkt cookie, in seconds. This differs from timeout inasmuch as timeout represents the lifetime of the ticket contained in the cookie, while this value represents the lifetime of the cookie itself. When this value is set, the cookies Max-Age and Expires settings will be set, allowing the auth_tkt cookie to last between browser sessions. It is typically nonsensical to set this to a value that is lower than timeout or reissue_time, although it is not explicitly prevented. Optional. 518
42.1. AUTHENTICATION POLICIES path Default: /. The path for which the auth_tkt cookie is valid. May be desirable if the application only serves part of a domain. Optional. http_only Default: False. Hide cookie from JavaScript by setting the HttpOnly ag. Not honored by all browsers. Optional. wild_domain Default: True. An auth_tkt cookie will be generated for the wildcard domain. Optional. hashalg Default: md5 (the literal string). Any hash algorithm supported by Pythons hashlib.new() function can be used as the hashalg. Cookies generated by different instances of AuthTktAuthenticationPolicy using different hashalg options are not compatible. Switching the hashalg will imply that all existing users with a valid cookie will be required to re-login. A warning is emitted at startup if an explicit hashalg is not passed. This is for backwards compatibility reasons. This option is available as of Pyramid 1.4. Optional.
md5 is the default for backwards compatibility reasons. However, if you dont specify md5 as the hashalg explicitly, a warning is issued at application startup time. An explicit value of sha512 is recommended for improved security, and sha512 will become the default in a future Pyramid version. debug Default: False. If debug is True, log messages to the Pyramid debug logger about the results of various authentication steps. The output from debugging is useful for reporting to maillist or IRC channels when asking for support. 519
42. PYRAMID.AUTHENTICATION Objects of this class implement the pyramid.interfaces.IAuthenticationPolicy. authenticated_userid(request) Return the authenticated userid or None. If no callback is registered, this will be the same as unauthenticated_userid. If a callback is registered, this will return the userid if and only if the callback returns a value that is not None. effective_principals(request) A list of effective principals derived from request. This will return a list of principals including, at least, pyramid.security.Everyone. If there is no authenticated userid, or the callback returns None, this will be the only principal:
return [Everyone]
interface
described
by
If the callback does not return None and an authenticated userid is found, then the principals will include pyramid.security.Authenticated, the authenticated_userid and the list of principals returned by the callback:
extra_principals = callback(userid, request) return [Everyone, Authenticated, userid] + extra_principals
forget(request) A list of headers which will delete appropriate cookies. remember(request, principal, **kw) Accepts the following kw args: tokens=<sequence-of-ascii-strings>.
max_age=<int-seconds>,
Return a list of headers which will set appropriate cookies on the response. unauthenticated_userid(request) The userid key within the auth_tkt cookie.
520
42.1. AUTHENTICATION POLICIES class RemoteUserAuthenticationPolicy(environ_key=REMOTE_USER, callback=None, debug=False) A Pyramid authentication policy which obtains data from the REMOTE_USER WSGI environment variable. Constructor Arguments environ_key Default: REMOTE_USER. The key in the WSGI environ which provides the userid. callback Default: None. A callback passed the userid and the request, expected to return None if the userid doesnt exist or a sequence of principal identiers (possibly empty) representing groups if the user does exist. If callback is None, the userid will be assumed to exist with no group principals. debug Default: False. If debug is True, log messages to the Pyramid debug logger about the results of various authentication steps. The output from debugging is useful for reporting to maillist or IRC channels when asking for support. Objects of this class implement the pyramid.interfaces.IAuthenticationPolicy. authenticated_userid(request) Return the authenticated userid or None. If no callback is registered, this will be the same as unauthenticated_userid. If a callback is registered, this will return the userid if and only if the callback returns a value that is not None. effective_principals(request) A list of effective principals derived from request. This will return a list of principals including, at least, pyramid.security.Everyone. If there is no authenticated userid, or the callback returns None, this will be the only principal: interface described by
521
42. PYRAMID.AUTHENTICATION
return [Everyone]
If the callback does not return None and an authenticated userid is found, then the principals will include pyramid.security.Authenticated, the authenticated_userid and the list of principals returned by the callback:
extra_principals = callback(userid, request) return [Everyone, Authenticated, userid] + extra_principals
forget(request) A no-op. The REMOTE_USER does not provide a protocol for forgetting the user. This will be application-specic and can be done somewhere else or in a subclass. remember(request, principal, **kw) A no-op. The REMOTE_USER does not provide a protocol for remembering the user. This will be application-specic and can be done somewhere else or in a subclass. unauthenticated_userid(request) The REMOTE_USER value found within the environ. class SessionAuthenticationPolicy(prex=auth., callback=None, debug=False) A Pyramid authentication policy which gets its data from the congured session. For this authentication policy to work, you will have to follow the instructions in the Sessions to congure a session factory. Constructor Arguments prefix A prex used when storing the authentication parameters in the session. Defaults to auth.. Optional. callback Default: None. A callback passed the userid and the request, expected to return None if the userid doesnt exist or a sequence of principal identiers (possibly empty) if the user does exist. If callback is None, the userid will be assumed to exist with no principals. Optional. debug
522
42.1. AUTHENTICATION POLICIES Default: False. If debug is True, log messages to the Pyramid debug logger about the results of various authentication steps. The output from debugging is useful for reporting to maillist or IRC channels when asking for support. authenticated_userid(request) Return the authenticated userid or None. If no callback is registered, this will be the same as unauthenticated_userid. If a callback is registered, this will return the userid if and only if the callback returns a value that is not None. effective_principals(request) A list of effective principals derived from request. This will return a list of principals including, at least, pyramid.security.Everyone. If there is no authenticated userid, or the callback returns None, this will be the only principal:
return [Everyone]
If the callback does not return None and an authenticated userid is found, then the principals will include pyramid.security.Authenticated, the authenticated_userid and the list of principals returned by the callback:
extra_principals = callback(userid, request) return [Everyone, Authenticated, userid] + extra_principals
forget(request) Remove the stored principal from the session. remember(request, principal, **kw) Store a principal in the session. class BasicAuthAuthenticationPolicy(check, realm=Realm, debug=False) A Pyramid authentication policy which uses HTTP standard basic authentication protocol to authenticate users. To use this policy you will need to provide a callback which checks the supplied user credentials against your source of login data. Constructor Arguments check 523
42. PYRAMID.AUTHENTICATION A callback function passed a username, password and request, in that order as positional arguments. Expected to return None if the userid doesnt exist or a sequence of principal identiers (possibly empty) if the user does exist. realm Default: "Realm". The Basic Auth Realm string. Usually displayed to the user by the browser in the login dialog. debug Default: False. If debug is True, log messages to the Pyramid debug logger about the results of various authentication steps. The output from debugging is useful for reporting to maillist or IRC channels when asking for support. Issuing a challenge Regular browsers will not send username/password credentials unless they rst receive a challenge from the server. The following recipe will register a view that will send a Basic Auth challenge to the user whenever there is an attempt to call a view which results in a Forbidden response:
from from from from pyramid.httpexceptions import HTTPForbidden pyramid.httpexceptions import HTTPUnauthorized pyramid.security import forget pyramid.view import view_config
authenticated_userid(request) Return the authenticated userid or None. If no callback is registered, this will be the same as unauthenticated_userid. If a callback is registered, this will return the userid if and only if the callback returns a value that is not None. effective_principals(request) A list of effective principals derived from request. This will return a list of principals including, at least, pyramid.security.Everyone. If there is no authenticated userid, or the callback returns None, this will be the only principal: 524
return [Everyone]
If the callback does not return None and an authenticated userid is found, then the principals will include pyramid.security.Authenticated, the authenticated_userid and the list of principals returned by the callback:
extra_principals = callback(userid, request) return [Everyone, Authenticated, userid] + extra_principals
forget(request) Returns challenge headers. This should be attached to a response to indicate that credentials are required. remember(request, principal, **kw) A no-op. Basic authentication does not provide a protocol for remembering the user. Credentials are sent on every request. unauthenticated_userid(request) The userid parsed from the Authorization request header. class RepozeWho1AuthenticationPolicy(identier_name=auth_tkt, callback=None) A Pyramid authentication policy which obtains data from the repoze.who 1.X WSGI API (the repoze.who.identity key in the WSGI environment). Constructor Arguments identifier_name Default: auth_tkt. The repoze.who plugin name that performs remember/forget. Optional. callback Default: None. A callback passed the repoze.who identity and the request, expected to return None if the user represented by the identity doesnt exist or a sequence of principal identiers (possibly empty) representing groups if the user does exist. If callback is None, the userid will be assumed to exist with no group principals. Objects of this class implement the pyramid.interfaces.IAuthenticationPolicy. interface described by
525
42. PYRAMID.AUTHENTICATION authenticated_userid(request) Return the authenticated userid or None. If no callback is registered, this will be the same as unauthenticated_userid. If a callback is registered, this will return the userid if and only if the callback returns a value that is not None. effective_principals(request) A list of effective principals derived from the identity. This will return a list of principals including, at least, pyramid.security.Everyone. If there is no identity, or the callback returns None, this will be the only principal. If the callback does not return None and an identity is found, then the principals will include pyramid.security.Authenticated, the authenticated_userid and the list of principals returned by the callback. forget(request) Forget the current authenticated user. Return headers that, if included in a response, will delete the cookie responsible for tracking the current user. remember(request, principal, **kw) Store the principal as repoze.who.userid. unauthenticated_userid(request) Return the repoze.who.userid key from the detected identity.
42.2. HELPER CLASSES class AuthTicket(secret, userid, ip, tokens=(), user_data=, time=None, cookie_name=auth_tkt, secure=False, hashalg=md5) This class represents an authentication token. You must pass in the shared secret, the userid, and the IP address. Optionally you can include tokens (a list of strings, representing role names), user_data, which is arbitrary data available for your own use in later scripts. Lastly, you can override the cookie name and timestamp. Once you provide all the arguments, use .cookie_value() to generate the appropriate authentication ticket. Usage:
exception AuthTktCookieHelper.BadTicket(msg, expected=None) Exception raised when a ticket cant be parsed. If we get far enough to determine what the expected digest should have been, expected is set. This should not be shown by default, but can be useful for debugging. AuthTktCookieHelper.forget(request) Return a set of expires Set-Cookie headers, which will destroy any existing auth_tkt cookie when attached to a response AuthTktCookieHelper.identify(request) Return a dictionary with authentication information, or None if no valid auth_tkt is attached to request static AuthTktCookieHelper.parse_ticket(secret, ticket, hashalg=md5) Parse the ticket, returning (timestamp, userid, tokens, user_data). ip,
If the ticket cannot be parsed, a BadTicket exception will be raised with an explanation. AuthTktCookieHelper.remember(request, userid, max_age=None, tokens=()) Return a set of Set-Cookie headers; when set into a response, these headers will represent a valid authentication ticket.
527
42. PYRAMID.AUTHENTICATION max_age The max age of the auth_tkt cookie, in seconds. When this value is set, the cookies Max-Age and Expires settings will be set, allowing the auth_tkt cookie to last between browser sessions. If this value is None, the max_age value provided to the helper itself will be used as the max_age value. Default: None. tokens A sequence of strings that will be placed into the auth_tkt tokens eld. Each string in the sequence must be of the Python str type and must match the regex ^[A-Za-z][A-Za-z0-9+_-]*$. Tokens are available in the returned identity when an auth_tkt is found in the request and unpacked. Default: ().
528
CHAPTER
FORTYTHREE
PYRAMID.CONFIG
class Configurator(registry=None, package=None, settings=None, root_factory=None, authentication_policy=None, authorization_policy=None, renderers=None, debug_logger=None, locale_negotiator=None, request_factory=None, renderer_globals_factory=None, default_permission=None, session_factory=None, default_view_mapper=None, autocommit=False, exceptionresponse_view=<function default_exceptionresponse_view at 0x428f230>, route_prex=None, introspection=True) A Congurator is used to congure a Pyramid application registry. The Congurator accepts a number of arguments: registry, package, settings, root_factory, authentication_policy, authorization_policy, renderers, debug_logger, locale_negotiator, request_factory, renderer_globals_factory, default_permission, session_factory, default_view_mapper, autocommit, exceptionresponse_view and route_prefix. If the registry argument is passed as a non-None value, it must be an instance of the pyramid.registry.Registry class representing the registry to congure. If registry is None, the congurator will create a pyramid.registry.Registry instance itself; it will also perform some default conguration that would not otherwise be done. After its construction, the congurator may be used to add further conguration to the registry.
If a registry is passed to the Congurator constructor, all other constructor arguments except package are ignored.
529
43. PYRAMID.CONFIG If the package argument is passed, it must be a reference to a Python package (e.g. sys.modules[thepackage]) or a dotted Python name to the same. This value is used as a basis to convert relative paths passed to various conguration methods, such as methods which accept a renderer argument, into absolute paths. If None is passed (the default), the package is assumed to be the Python package in which the caller of the Configurator constructor lives. If the settings argument is passed, it should be a Python dictionary representing the deployment settings for this application. These are later retrievable using the pyramid.registry.Registry.settings attribute (aka request.registry.settings). If the root_factory argument is passed, it should be an object representing the default root factory for your application or a dotted Python name to the same. If it is None, a default root factory will be used. If authentication_policy is passed, it should be an instance of an authentication policy or a dotted Python name to the same. If authorization_policy is passed, it should be an instance of an authorization policy or a dotted Python name to the same.
A ConfigurationError will be raised when an authorization policy is supplied without also supplying an authentication policy (authorization requires authentication).
If renderers is passed, it should be a list of tuples representing a set of renderer factories which should be congured into this application (each tuple representing a set of positional values that should be passed to pyramid.config.Configurator.add_renderer()). If it is not passed, a default set of renderer factories is used. If debug_logger is not passed, a default debug logger that logs to a logger will be used (the logger name will be the package name of the caller of this congurator). If it is passed, it should be an instance of the logging.Logger (PEP 282) standard library class or a Python logger name. The debug logger is used by Pyramid itself to log warnings and authorization debugging information. If locale_negotiator is passed, it should be a locale negotiator implementation or a dotted Python name to same. See Using a Custom Locale Negotiator. If request_factory is passed, it should be a request factory implementation or a dotted Python name to the same. See Changing the Request Factory. By default it is None, which means use the default request factory. 530
If renderer_globals_factory is passed, it should be a renderer globals factory implementation or a dotted Python name to the same. See Adding Renderer Globals (Deprecated). By default, it is None, which means use no renderer globals factory.
as of Pyramid 1.1, renderer_globals_factory is deprecated. Instead, use a BeforeRender event subscriber as per Using The Before Render Event.
If default_permission is passed, it should be a permission string to be used as the default permission for all view conguration registrations performed against this Congurator. An example of a permission string:view. Adding a default permission makes it unnecessary to protect each view conguration with an explicit permission, unless your application policy requires some exception for a particular view. By default, default_permission is None, meaning that view congurations which do not explicitly declare a permission will always be executable by entirely anonymous users (any authorization policy in effect is ignored). See also Setting a Default Permission. If session_factory is passed, it should be an object which implements the session factory interface. If a nondefault value is passed, the session_factory will be used to create a session object when request.session is accessed. Note that the same outcome can be achieved by calling pyramid.config.Configurator.set_session_factory(). By default, this argument is None, indicating that no session factory will be congured (and thus accessing request.session will throw an error) unless set_session_factory is called later during conguration. If autocommit is True, every method called on the congurator will cause an immediate action, and no conguration conict detection will be used. If autocommit is False, most methods of the congurator will defer their action until pyramid.config.Configurator.commit() is called. When pyramid.config.Configurator.commit() is called, the actions implied by the called methods will be checked for conguration conicts unless autocommit is True. If a conict is detected a ConfigurationConflictError will be raised. Calling pyramid.config.Configurator.make_wsgi_app() always implies a nal commit. If default_view_mapper is passed, it will be used as the default view mapper factory for view congurations that dont otherwise specify one (see pyramid.interfaces.IViewMapperFactory). If a default_view_mapper is not passed, a superdefault view mapper will be used. If exceptionresponse_view is passed, it must be a view callable or None. If it is a view callable, it will be used as an exception view callable when an exception response is raised. If exceptionresponse_view is None, no exception response view will be registered, and all raised exception responses will be bubbled up to Pyramids caller. By default, the 531
43. PYRAMID.CONFIG pyramid.httpexceptions.default_exceptionresponse_view function is used as the exceptionresponse_view. This argument is new in Pyramid 1.1. If route_prefix is passed, all routes added with pyramid.config.Configurator.add_route() will have the specied path prepended to their pattern. This parameter is new in Pyramid 1.2. If introspection is passed, it must be a boolean value. If its True, introspection values during actions will be kept for use for tools like the debug toolbar. If its False, introspection values provided by registrations will be ignored. By default, it is True. This parameter is new as of Pyramid 1.3. Controlling Conguration State commit() Commit any pending conguration actions. If a conguration conict is detected in the pending conguration actions, this method will raise a ConfigurationConflictError; within the traceback of this error will be information about the source of the conict, usually including le names and line numbers of the cause of the conguration conicts. begin(request=None) Indicate that application or test conguration has begun. This pushes a dictionary containing the application registry implied by registry attribute of this congurator and the request implied by the request argument on to the thread local stack consulted by various pyramid.threadlocal API functions. end() Indicate that application or test conguration has ended. This pops the last value pushed on to the thread local stack (usually by the begin method) and returns that value. include(callable, route_prex=None) Include a conguration callables, to support imperative application extensibility.
In versions of Pyramid prior to 1.2, this function accepted *callables, but this has been changed to support only a single callable.
A conguration callable should be a callable that accepts a single argument named config, which will be an instance of a Congurator (be warned that it will not be the same congurator instance on which you call this method, however). The code
532
which runs as the result of calling the callable should invoke methods on the congurator passed to it which add conguration state. The return value of a callable will be ignored. Values allowed to be presented via the callable argument to this method: any callable Python object or any dotted Python name which resolves to a callable Python object. It may also be a Python module, in which case, the module will be searched for a callable named includeme, which will be treated as the conguration callable. For example, if the includeme function below lives in a module named myapp.myconfig:
1 2 3 4 5 6 7 8
# myapp.myconfig module def my_view(request): from pyramid.response import Response return Response(OK) def includeme(config): config.add_view(my_view)
You might cause it be included within your Pyramid application like so:
1 2 3 4 5
from pyramid.config import Configurator def main(global_config, **settings): config = Configurator() config.include(myapp.myconfig.includeme)
Because the function is named includeme, the function name can also be omitted from the dotted name reference:
1 2 3 4 5
from pyramid.config import Configurator def main(global_config, **settings): config = Configurator() config.include(myapp.myconfig)
533
43. PYRAMID.CONFIG Included conguration statements will be overridden by local conguration statements if an included callable causes a conguration conict by registering something with the same conguration parameters. If the route_prefix is supplied, it must be a string. Any calls to pyramid.config.Configurator.add_route() within the included callable will have their pattern prexed with the value of route_prefix. This can be used to help mount a set of routes at a different location than the included callables author intended while still maintaining the same route names. For example:
1 2 3 4 5 6 7 8
from pyramid.config import Configurator def included(config): config.add_route(show_users, /show) def main(global_config, **settings): config = Configurator() config.include(included, route_prefix=/users)
In the above conguration, the show_users route will have an effective route pattern of /users/show, instead of /show because the route_prefix argument will be prepended to the pattern. The route_prefix parameter is new as of Pyramid 1.2. make_wsgi_app() Commits any pending conguration statements, sends a pyramid.events.ApplicationCreated event to all listeners, adds this congurations registry to pyramid.config.global_registries, and returns a Pyramid WSGI application representing the committed conguration state. scan(package=None, categories=None, onerror=None, ignore=None, **kw) Scan a Python package and any of its subpackages for objects marked with conguration decoration such as pyramid.view.view_config. Any decorated object found will inuence the current conguration state. The package argument should be a Python package or module object (or a dotted Python name which refers to such a package or module). If package is None, the package of the caller is used.
534
The categories argument, if provided, should be the Venusian scan categories to use during scanning. Providing this argument is not often necessary; specifying scan categories is an extremely advanced usage. By default, categories is None which will execute all Venusian decorator callbacks including Pyramidrelated decorators such as pyramid.view.view_config. See the Venusian documentation for more information about limiting a scan by using an explicit set of categories. The onerror argument, if provided, should be a Venusian onerror callback function. The onerror function is passed to venusian.Scanner.scan() to inuence error behavior when an exception is raised during the scanning process. See the Venusian documentation for more information about onerror callbacks. The ignore argument, if provided, should be a Venusian ignore value. Providing an ignore argument allows the scan to ignore particular modules, packages, or global objects during a scan. ignore can be a string or a callable, or a list containing strings or callables. The simplest usage of ignore is to provide a module or package by providing a full path to its dotted name. For example: config.scan(ignore=my.module.subpackage) would ignore the my.module.subpackage package during a scan, which would prevent the subpackage and any of its submodules from being imported and scanned. See the Venusian documentation for more information about the ignore argument.
To perform a scan, Pyramid creates a Venusian Scanner object. The kw argument represents a set of keyword arguments to pass to the Venusian Scanner objects constructor. See the venusian documentation (its Scanner class) for more information about the constructor. By default, the only keyword arguments passed to the Scanner constructor are {config:self} where self is this congurator object. This services the requirement of all built-in Pyramid decorators, but extension systems may require additional arguments. Providing this argument is not often necessary; its an advanced usage.
535
43. PYRAMID.CONFIG add_route(name, pattern=None, view=None, view_for=None, permission=None, factory=None, for_=None, header=None, xhr=None, accept=None, path_info=None, request_method=None, request_param=None, traverse=None, custom_predicates=(), view_permission=None, renderer=None, view_renderer=None, view_context=None, view_attr=None, use_global_views=False, path=None, pregenerator=None, static=False, **predicates) Add a route conguration to the current conguration state, as well as possibly a view conguration to be used to specify a view callable that will be invoked when this route matches. The arguments to this method are divided into predicate, non-predicate, and view-related types. Route predicate arguments narrow the circumstances in which a route will be match a request; non-predicate arguments are informational. Non-Predicate Arguments name The name of the route, e.g. myroute. This attribute is required. It must be unique among all dened routes in a given application. factory A Python object (often a function or a class) or a dotted Python name which refers to the same object that will generate a Pyramid root resource object when this route matches. For example, mypackage.resources.MyFactory. If this argument is not specied, a default root factory will be used. See The Resource Tree for more information about root factories. traverse If you would like to cause the context to be something other than the root object when this route matches, you can spell a traversal pattern as the traverse argument. This traversal pattern will be used as the traversal path: traversal will begin at the root object implied by this route (either the global root, or the object returned by the factory associated with this route). The syntax of the traverse argument is the same as it is for pattern. For example, if the pattern provided to add_route is articles/{article}/edit, and the traverse argument provided to add_route is /{article}, when a request comes in that causes the route to match in such a way that the article match value is 1 (when the request URI is /articles/1/edit), the traversal path will be generated as /1. This means that the root objects __getitem__ will be called with the name 1 during the traversal phase. If the 1 object exists, it will become the context of the request. Traversal has more information about traversal. 536
If the traversal path contains segment marker names which are not present in the pattern argument, a runtime error will occur. The traverse pattern should not contain segment markers that do not exist in the pattern argument. A similar combining of routing and traversal is available when a route is matched which contains a *traverse remainder marker in its pattern (see Using *traverse In a Route Pattern). The traverse argument to add_route allows you to associate route patterns with an arbitrary traversal path without using a *traverse remainder marker; instead you can use other match information. Note that the traverse argument to add_route is ignored when attached to a route that has a *traverse remainder marker in its pattern. pregenerator This option should be a callable object that implements the pyramid.interfaces.IRoutePregenerator interface. A pregenerator is a callable called by the pyramid.request.Request.route_url() function to augment or replace the arguments it is passed when generating a URL for the route. This is a feature not often used directly by applications, it is meant to be hooked by frameworks that use Pyramid as a base. use_global_views When a request matches this route, and view lookup cannot nd a view which has a route_name predicate argument that matches the route, try to fall back to using a view that otherwise matches the context, request, and view name (but which does not match the route_name predicate). static If static is True, this route will never match an incoming request; it will only be useful for URL generation. By default, static is False. See Static Routes. New in Pyramid 1.1. Predicate Arguments pattern The pattern of the route e.g. ideas/{idea}. This argument is required. See Route Pattern Syntax for information about the syntax of route patterns. If the pattern doesnt match the current URL, route matching continues. For backwards compatibility purposes (as of Pyramid 1.0), a path keyword argument passed to this function will be used to represent the pattern value if the pattern argument is None. If both path and pattern are passed, pattern wins. 537
43. PYRAMID.CONFIG xhr This value should be either True or False. If this value is specied and is True, the request must possess an HTTP_X_REQUESTED_WITH (aka X-Requested-With) header for this route to match. This is useful for detecting AJAX requests issued from jQuery, Prototype and other Javascript libraries. If this predicate returns False, route matching continues. request_method A string representing an HTTP method name, e.g. GET, POST, HEAD, DELETE, PUT or a tuple of elements containing HTTP method names. If this argument is not specied, this route will match if the request has any request method. If this predicate returns False, route matching continues.
The ability to pass a tuple of items as request_method is new as of Pyramid 1.2. Previous versions allowed only a string. path_info This value represents a regular expression pattern that will be tested against the PATH_INFO WSGI environment variable. If the regex matches, this predicate will return True. If this predicate returns False, route matching continues. request_param This value can be any string. A view declaration with this argument ensures that the associated route will only match when the request has a key in the request.params dictionary (an HTTP GET or POST variable) that has a name which matches the supplied value. If the value supplied as the argument has a = sign in it, e.g. request_param="foo=123", then the key (foo) must both exist in the request.params dictionary, and the value must match the right hand side of the expression (123) for the route to match the current request. If this predicate returns False, route matching continues. header This argument represents an HTTP header name or a header name/value pair. If the argument contains a : (colon), it will be considered a name/value pair (e.g. User-Agent:Mozilla/.* or Host:localhost). If the value contains a colon, the value portion should be a regular expression. If the value does not contain a colon, the entire value will be considered to be the header name (e.g. If-Modified-Since). If the value evaluates to a header name only without a value, the header specied by the name must be present in the request for this predicate to be true. If the value evaluates to a header name/value pair, the header specied by the name must be present in the request and the regular expression specied as the value must match the 538
header value. Whether or not the value represents a header name or a header name/value pair, the case of the header name is not signicant. If this predicate returns False, route matching continues. accept This value represents a match query for one or more mimetypes in the Accept HTTP request header. If this value is specied, it must be in one of the following forms: a mimetype match token in the form text/plain, a wildcard mimetype match token in the form text/* or a match-all wildcard mimetype match token in the form */*. If any of the forms matches the Accept header of the request, this predicate will be true. If this predicate returns False, route matching continues. effective_principals If specied, this value should be a principal identier or a sequence of principal identiers. If the pyramid.security.effective_principals() method indicates that every principal named in the argument list is present in the current request, this predicate will return True; otherwise it will return False. For example: effective_principals=pyramid.security.Authenticated or effective_principals=(fred, group:admins). New in version 1.4a4. custom_predicates This value should be a sequence of references to custom predicate callables. Use custom predicates when no set of predened predicates does what you need. Custom predicates can be combined with predened predicates as necessary. Each custom predicate callable should accept two arguments: info and request and should return either True or False after doing arbitrary evaluation of the info and/or the request. If all custom and non-custom predicate callables return True the associated route will be considered viable for a given request. If any predicate callable returns False, route matching continues. Note that the value info passed to a custom route predicate is a dictionary containing matching information; see Custom Route Predicates for more information about info. predicates Pass a key/value pair here to use a third-party predicate registered via pyramid.config.Configurator.add_view_predicate(). More than one key/value pair can be used at the same time. See View and Route Predicates for more information about third-party predicates. This argument is new as of Pyramid 1.4. View-Related Arguments
539
43. PYRAMID.CONFIG
The arguments described below have been deprecated as of Pyramid 1.1. Do not use these for new development; they should only be used to support older code bases which depend upon them. Use a separate call to pyramid.config.Configurator.add_view() to associate a view with a route using the route_name argument.
A Python object or dotted Python name to the same object that will be used as a view callable when this route matches. e.g. mypackage.views.my_view. view_context Deprecated as of Pyramid 1.1.
A class or an interface or dotted Python name to the same object which the context of the view should match for the view named by the route to be used. This argument is only useful if the view attribute is used. If this attribute is not specied, the default (None) will be used. If the view argument is not provided, this argument has no effect. This attribute can also be spelled as for_ or view_for. view_permission Deprecated as of Pyramid 1.1.
The permission name required to invoke the view associated with this route. e.g. edit. (see Using Pyramid Security With URL Dispatch for more information about permissions). If the view attribute is not provided, this argument has no effect. This argument can also be spelled as permission. view_renderer
540
Deprecated as of Pyramid 1.1. This is either a single string term (e.g. json) or a string implying a path or asset specication (e.g. templates/views.pt). If the renderer value is a single term (does not contain a dot .), the specied term will be used to look up a renderer implementation, and that renderer implementation will be used to construct a response from the view return value. If the renderer term contains a dot (.), the specied term will be treated as a path, and the lename extension of the last element in the path will be used to look up the renderer implementation, which will be passed the full path. The renderer implementation will be used to construct a response from the view return value. See Writing View Callables Which Use a Renderer for more information. If the view argument is not provided, this argument has no effect. This argument can also be spelled as renderer. view_attr Deprecated as of Pyramid 1.1. The view machinery defaults to using the __call__ method of the view callable (or the function itself, if the view callable is a function) to obtain a response dictionary. The attr value allows you to vary the method attribute used to obtain the response. For example, if your view was a class, and the class has a method named index and you wanted to use this method instead of the class __call__ method to return the response, youd say attr="index" in the view conguration for the view. This is most useful when the view denition is a class. If the view argument is not provided, this argument has no effect. add_static_view(name, path, **kw) Add a view used to render static assets such as images and CSS les. The name argument is a string representing an application-relative local URL prex. It may alternately be a full URL. The path argument is the path on disk where the static les reside. This can be an absolute path, a package-relative path, or a asset specication. The cache_max_age keyword argument is input to set the Expires and Cache-Control headers for static assets served. Note that this argument has 541
43. PYRAMID.CONFIG no effect when the name is a url prex. By default, this argument is None, meaning that no particular Expires or Cache-Control headers are set in the response. The permission keyword argument is used to specify the permission required by a user to execute the static view. By default, it is the string pyramid.security.NO_PERMISSION_REQUIRED, a special sentinel which indicates that, even if a default permission exists for the current application, the static view should be renderered to completely anonymous users. This default value is permissive because, in most web apps, static assets seldom need protection from viewing. If permission is specied, the security checking will be performed against the default root factory ACL. Any other keyword arguments sent to add_static_view are passed on to pyramid.config.Configurator.add_route() (e.g. factory, perhaps to dene a custom factory with a custom ACL for this static view). Usage The add_static_view function is typically used in conjunction with the pyramid.request.Request.static_url() method. add_static_view adds a view which renders a static asset when some URL is visited; pyramid.request.Request.static_url() generates a URL to that asset. The name argument to add_static_view is usually a simple URL prex (e.g. images). When this is the case, the pyramid.request.Request.static_url() API will generate a URL which points to a Pyramid view, which will serve up a set of assets that live in the package itself. For example:
add_static_view(images, mypackage:images/)
Code that registers such a view can generate URLs to the view via pyramid.request.Request.static_url():
request.static_url(mypackage:images/logo.png)
542
When add_static_view is called with a name argument that represents a URL prex, as it is above, subsequent calls to pyramid.request.Request.static_url() with paths that start with the path argument passed to add_static_view will generate a URL something like http://<Pyramid app URL>/images/logo.png, which will cause the logo.png le in the images subdirectory of the mypackage package to be served. add_static_view can alternately be used with a name argument which is a URL, causing static assets to be served from an external webserver. This happens when the name argument is a fully qualied URL (e.g. starts with http:// or similar). In this mode, the name is used as the prex of the full URL when generating a URL using pyramid.request.Request.static_url(). For example, if add_static_view is called like so:
add_static_view(http://example.com/images, mypackage:images/)
Subsequently, the URLs generated by pyramid.request.Request.static_url() for that static view will be prexed with http://example.com/images:
static_url(mypackage:images/logo.png, request)
When add_static_view is called with a name argument that is the URL http://example.com/images, subsequent calls to pyramid.request.Request.static_url() with paths that start with the path argument passed to add_static_view will generate a URL something like http://example.com/logo.png. The external webserver listening on example.com must be itself congured to respond properly to such a request. See Serving Static Assets for more information. add_view(view=None, name=, for_=None, permission=None, request_type=None, route_name=None, request_method=None, request_param=None, containment=None, attr=None, renderer=None, wrapper=None, xhr=None, accept=None, header=None, path_info=None, custom_predicates=(), context=None, decorator=None, mapper=None, http_cache=None, match_param=None, check_csrf=None, **predicates) Add a view conguration to the current conguration state. Arguments to 543
43. PYRAMID.CONFIG add_view are broken down below into predicate arguments and non-predicate arguments. Predicate arguments narrow the circumstances in which the view callable will be invoked when a request is presented to Pyramid; non-predicate arguments are informational. Non-Predicate Arguments view A view callable or a dotted Python name which refers to a view callable. This argument is required unless a renderer argument also exists. If a renderer argument is passed, and a view argument is not provided, the view callable defaults to a callable that returns an empty dictionary (see Writing View Callables Which Use a Renderer). permission A permission that the user must possess in order to invoke the view callable. See Conguring View Security for more information about view security and permissions. This is often a string like view or edit. If permission is omitted, a default permission may be used for this view registration if one was named as the pyramid.config.Configurator constructors default_permission argument, or if pyramid.config.Configurator.set_default_permission() was used prior to this view registration. Pass the value pyramid.security.NO_PERMISSION_REQUIRED as the permission argument to explicitly indicate that the view should always be executable by entirely anonymous users, regardless of the default permission, bypassing any authorization policy that may be in effect. attr This knob is most useful when the view denition is a class. The view machinery defaults to using the __call__ method of the view callable (or the function itself, if the view callable is a function) to obtain a response. The attr value allows you to vary the method attribute used to obtain the response. For example, if your view was a class, and the class has a method named index and you wanted to use this method instead of the class __call__ method to return the response, youd say attr="index" in the view conguration for the view. renderer This is either a single string term (e.g. json) or a string implying a path or asset specication (e.g. templates/views.pt) naming a renderer implementation. If the renderer value does not contain a dot ., the specied string will be used to look up a renderer implementation, and that renderer implementation will be used to construct a response from the view 544
return value. If the renderer value contains a dot (.), the specied term will be treated as a path, and the lename extension of the last element in the path will be used to look up the renderer implementation, which will be passed the full path. The renderer implementation will be used to construct a response from the view return value. Note that if the view itself returns a response (see View Callable Responses), the specied renderer implementation is never called. When the renderer is a path, although a path is usually just a simple relative pathname (e.g. templates/foo.pt, implying that a template named foo.pt is in the templates directory relative to the directory of the current package of the Congurator), a path can be absolute, starting with a slash on UNIX or a drive letter prex on Windows. The path can alternately be a asset specication in the form some.dotted.package_name:relative/path, making it possible to address template assets which live in a separate package. The renderer attribute is optional. If it is not dened, the null renderer is assumed (no rendering is performed and the value is passed back to the upstream Pyramid machinery unmodied). http_cache This feature is new as of Pyramid 1.1.
When you supply an http_cache value to a view conguration, the Expires and Cache-Control headers of a response generated by the associated view callable are modied. The value for http_cache may be one of the following: A nonzero integer. If its a nonzero integer, its treated as a number of seconds. This number of seconds will be used to compute the Expires header and the Cache-Control: max-age parameter of responses to requests which call this view. For example: http_cache=3600 instructs the requesting browser to cache this response for an hour, please. A datetime.timedelta instance. If its a datetime.timedelta instance, it will be converted into a number of seconds, and that number of seconds will be used to compute the Expires header and the Cache-Control: max-age parameter of responses to requests which call this view. For example: http_cache=datetime.timedelta(days=1) instructs the requesting browser to cache this response for a day, please. 545
43. PYRAMID.CONFIG Zero (0). If the value is zero, the Cache-Control and Expires headers present in all responses from this view will be composed such that client browser cache (and any intermediate caches) are instructed to never cache the response. A two-tuple. If its a two tuple (e.g. http_cache=(1, {public:True})), the rst value in the tuple may be a nonzero integer or a datetime.timedelta instance; in either case this value will be used as the number of seconds to cache the response. The second value in the tuple must be a dictionary. The values present in the dictionary will be used as input to the Cache-Control response header. For example: http_cache=(3600, {public:True}) means cache for an hour, and add public to the Cache-Control header of the response. All keys and values supported by the webob.cachecontrol.CacheControl interface may be added to the dictionary. Supplying {public:True} is equivalent to calling response.cache_control.public = True. Providing a non-tuple value as http_cache is equivalent to calling response.cache_expires(value) within your views body. Providing a two-tuple value as http_cache is equivalent to calling response.cache_expires(value[0], **value[1]) within your views body. If you wish to avoid inuencing, the Expires header, and instead wish to only inuence Cache-Control headers, pass a tuple as http_cache with the rst element of None, e.g.: (None, {public:True}). If you wish to prevent a view that uses http_cache in its conguration from having its caching response headers changed by this machinery, set response.cache_control.prevent_auto = True before returning the response from the view. This effectively disables any HTTP caching done by http_cache for that response. wrapper The view name of a different view conguration which will receive the response body of this view as the request.wrapped_body attribute of its own request, and the response returned by this view as the request.wrapped_response attribute of its own request. Using a wrapper makes it possible to chain views together to form a composite response. The response of the outermost wrapper view will be returned to the user. The wrapper view will be found as any view is found: see View Conguration. The best wrapper view will be found based on the lookup ordering: under the hood this wrapper view is looked up via pyramid.view.render_view_to_response(context, request, wrapper_viewname). The context and request of a 546
wrapper view is the same context and request of the inner view. If this attribute is unspecied, no view wrapping is done. decorator A dotted Python name to function (or the function itself, or an iterable of the aforementioned) which will be used to decorate the registered view callable. The decorator function(s) will be called with the view callable as a single argument. The view callable it is passed will accept (context, request). The decorator(s) must return a replacement view callable which also accepts (context, request). If decorator is an iterable, the callables will be combined and used in the order provided as a decorator. For example:
Is similar to doing:
Except with the existing benets of decorator= (having a common decorator syntax for all view calling conventions and not having to think about preserving function attributes such as __name__ and __module__ within decorator logic). Passing an iterable is only supported as of Pyramid 1.4a4. mapper A Python object or dotted Python name which refers to a view mapper, or None. By default it is None, which indicates that the view should use the default view mapper. This plug-point is useful for Pyramid extension developers, but its not very useful for civilians who are just developing stock Pyramid applications. Pay no attention to the man behind the curtain.
547
43. PYRAMID.CONFIG Predicate Arguments name The view name. Read Traversal to understand the concept of a view name. context An object or a dotted Python name referring to an interface or class object that the context must be an instance of, or the interface that the context must provide in order for this view to be found and called. This predicate is true when the context is an instance of the represented class or if the context provides the represented interface; it is otherwise false. This argument may also be provided to add_view as for_ (an older, still-supported spelling). route_name This value must match the name of a route conguration declaration (see URL Dispatch) that must match before this view will be called. request_type This value should be an interface that the request must provide in order for this view to be found and called. This value exists only for backwards compatibility purposes. request_method This value can be one of the strings GET, POST, PUT, DELETE, or HEAD representing an HTTP REQUEST_METHOD, or a tuple containing one or more of these strings. A view declaration with this argument ensures that the view will only be called when the requests method attribute (aka the REQUEST_METHOD of the WSGI environment) string matches a supplied value. Note that use of GET also implies that the view will respond to HEAD as of Pyramid 1.4.
The ability to pass a tuple of items as request_method is new as of Pyramid 1.2. Previous versions allowed only a string. request_param This value can be any string or any sequence of strings. A view declaration with this argument ensures that the view will only be called when the request has a key in the request.params dictionary (an HTTP GET or POST variable) that has a name which matches the supplied value (if the value is a string) or values (if the value is a tuple). If any value supplied has a = sign in it, e.g. request_param="foo=123", then the key (foo) must both exist in the request.params dictionary, and the value must match the right hand side of the expression (123) for the view to match the current request. match_param
548
This value can be a string of the format key=value or a tuple containing one or more of these strings. A view declaration with this argument ensures that the view will only be called when the request has key/value pairs in its matchdict that equal those supplied in the predicate. e.g. match_param="action=edit" would require the action parameter in the matchdict match the right hand side of the expression (edit) for the view to match the current request. If the match_param is a tuple, every key/value pair must match for the predicate to pass. containment This value should be a Python class or interface (or a dotted Python name) that an object in the lineage of the context must provide in order for this view to be found and called. The nodes in your object graph must be location-aware to use this feature. See Location-Aware Resources for more information about location-awareness. xhr This value should be either True or False. If this value is specied and is True, the request must possess an HTTP_X_REQUESTED_WITH (aka X-Requested-With) header that has the value XMLHttpRequest for this view to be found and called. This is useful for detecting AJAX requests issued from jQuery, Prototype and other Javascript libraries. accept The value of this argument represents a match query for one or more mimetypes in the Accept HTTP request header. If this value is specied, it must be in one of the following forms: a mimetype match token in the form text/plain, a wildcard mimetype match token in the form text/* or a match-all wildcard mimetype match token in the form */*. If any of the forms matches the Accept header of the request, this predicate will be true. header This value represents an HTTP header name or a header name/value pair. If the value contains a : (colon), it will be considered a name/value pair (e.g. User-Agent:Mozilla/.* or Host:localhost). The value portion should be a regular expression. If the value does not contain a colon, the entire value will be considered to be the header name (e.g. If-Modified-Since). If the value evaluates to a header name only without a value, the header specied by the name must be present in the 549
43. PYRAMID.CONFIG request for this predicate to be true. If the value evaluates to a header name/value pair, the header specied by the name must be present in the request and the regular expression specied as the value must match the header value. Whether or not the value represents a header name or a header name/value pair, the case of the header name is not signicant. path_info This value represents a regular expression pattern that will be tested against the PATH_INFO WSGI environment variable. If the regex matches, this predicate will be True. check_csrf If specied, this value should be one of None, True, False, or a string representing the check name. If the value is True or a string, CSRF checking will be performed. If the value is False or None, CSRF checking will not be performed. If the value provided is a string, that string will be used as the check name. If the value provided is True, csrf_token will be used as the check name. If CSRF checking is performed, the checked value will be the value of request.params[check_name]. This value will be compared against the value of request.session.get_csrf_token(), and the check will pass if these two values are the same. If the check passes, the associated view will be permitted to execute. If the check fails, the associated view will not be permitted to execute. Note that using this feature requires a session factory to have been congured. New in version 1.4a2. physical_path If specied, this value should be a string or a tuple representing the physical path of the context found via traversal for this predicate to match as true. For example: physical_path=/ or physical_path=/a/b/c or physical_path=(, a, b, c). This is not a path prex match or a regex, its a wholepath match. Its useful when you want to always potentially show a view when some object is traversed to, but you cant be sure about what kind of object it will be, so you cant use the context predicate. The individual path elements inbetween slash characters or in tuple elements should be the Unicode representation of the name of the resource and should not be encoded in any way. New in version 1.4a3. effective_principals If specied, this value should be a principal identier or a sequence of principal identiers. If the pyramid.security.effective_principals() method 550
indicates that every principal named in the argument list is present in the current request, this predicate will return True; otherwise it will return False. For example: effective_principals=pyramid.security.Authenticated or effective_principals=(fred, group:admins). New in version 1.4a4. custom_predicates This value should be a sequence of references to custom predicate callables. Use custom predicates when no set of predened predicates do what you need. Custom predicates can be combined with predened predicates as necessary. Each custom predicate callable should accept two arguments: context and request and should return either True or False after doing arbitrary evaluation of the context and/or the request. The predicates argument to this method and the ability to register third-party view predicates via pyramid.config.Configurator.add_view_predicate() obsoletes this argument, but it is kept around for backwards compatibility. predicates Pass a key/value pair here to use a third-party predicate registered via pyramid.config.Configurator.add_view_predicate(). More than one key/value pair can be used at the same time. See View and Route Predicates for more information about third-party predicates. add_notfound_view(view=None, attr=None, renderer=None, wrapper=None, route_name=None, request_type=None, request_method=None, request_param=None, containment=None, xhr=None, accept=None, header=None, path_info=None, custom_predicates=(), decorator=None, mapper=None, match_param=None, append_slash=False, **predicates) Add a default notfound view to the current conguration state. The view will be called when Pyramid or application code raises an pyramid.httpexceptions.HTTPForbidden exception (e.g. when a view cannot be found for the request). The simplest example is:
def notfound(request): return Response(Not Found, status=404 Not Found) config.add_notfound_view(notfound)
All arguments except append_slash have the same meaning as pyramid.config.Configurator.add_view() and each predicate argument restricts the set of circumstances under which this notfound view will be invoked. Unlike pyramid.config.Configurator.add_view(), this 551
43. PYRAMID.CONFIG method will raise an exception if passed name, permission, context, for_, or http_cache keyword arguments. These argument values make no sense in the context of a notfound view. If append_slash is True, when this notfound view is invoked, and the current path info does not end in a slash, the notfound logic will attempt to nd a route that matches the requests path info sufxed with a slash. If such a route exists, Pyramid will issue a redirect to the URL implied by the route; if it does not, Pyramid will return the result of the view callable provided as view, as normal.
This method is new as of Pyramid 1.3. add_forbidden_view(view=None, attr=None, renderer=None, wrapper=None, route_name=None, request_type=None, request_method=None, request_param=None, containment=None, xhr=None, accept=None, header=None, path_info=None, custom_predicates=(), decorator=None, mapper=None, match_param=None, **predicates) Add a forbidden view to the current conguration state. The view will be called when Pyramid or application code raises a pyramid.httpexceptions.HTTPForbidden exception and the set of circumstances implied by the predicates provided are matched. The simplest example is:
def forbidden(request): return Response(Forbidden, status=403 Forbidden) config.add_forbidden_view(forbidden)
All arguments have the same meaning as pyramid.config.Configurator.add_view() and each predicate argument restricts the set of circumstances under which this notfound view will be invoked. Unlike pyramid.config.Configurator.add_view(), this method will raise an exception if passed name, permission, context, for_, or http_cache keyword arguments. These argument values make no sense in the context of a forbidden view. This method is new as of Pyramid 1.3. Adding an Event Subscriber 552
add_subscriber(subscriber, iface=None, **predicates) Add an event subscriber for the event stream implied by the supplied iface interface. The subscriber argument represents a callable object (or a dotted Python name which identies a callable); it will be called with a single object event whenever Pyramid emits an event associated with the iface, which may be an interface or a class or a dotted Python name to a global object representing an interface or a class. Using the default iface value, None will cause the subscriber to be registered for all event types. See Using Events for more information about events and subscribers. Any number of predicate keyword arguments may be passed in **predicates. Each predicate named will narrow the set of circumstances that the subscriber will be invoked. Each named predicate must have been registered via pyramid.config.Configurator.add_subscriber_predicate() before it can be used. See Subscriber Predicates for more information. THe **predicates argument is new as of Pyramid 1.4. Using Security set_authentication_policy(policy) Override the Pyramid authentication policy in the current conguration. The policy argument must be an instance of an authentication policy or a dotted Python name that points at an instance of an authentication policy.
Using the authentication_policy argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose. set_authorization_policy(policy) Override the Pyramid authorization policy in the current conguration. The policy argument must be an instance of an authorization policy or a dotted Python name that points at an instance of an authorization policy.
Using the authorization_policy argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose.
553
43. PYRAMID.CONFIG set_default_permission(permission) Set the default permission to be used by all subsequent view conguration registrations. permission should be a permission string to be used as the default permission. An example of a permission string:view. Adding a default permission makes it unnecessary to protect each view conguration with an explicit permission, unless your application policy requires some exception for a particular view. If a default permission is not set, views represented by view conguration registrations which do not explicitly declare a permission will be executable by entirely anonymous users (any authorization policy is ignored). Later calls to this method override will conict with earlier calls; there can be only one default permission active at a time within an application.
If a default permission is in effect, view congurations meant to create a truly anonymously accessible view (even exception view views) must use the value of the permission importable as pyramid.security.NO_PERMISSION_REQUIRED. When this string is used as the permission for a view conguration, the default permission is ignored, and the view is registered, making it available to all callers regardless of their credentials.
Using the default_permission argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose.
add_permission(permission_name) A congurator directive which registers a free-standing permission without associating it with a view callable. This can be used so that the permission shows up in the introspectable data under the permissions category (permissions mentioned via add_view already end up in there). For example:
554
Extending the Request Object add_request_method(callable=None, name=None, erty=False, reify=False) Add a property or method to the request object. prop-
When adding a method to the request, callable may be any function that receives the request object as the rst parameter. If name is None then it will be computed from the name of the callable. When adding a property to the request, callable can either be a callable that accepts the request as its single positional parameter, or it can be a property descriptor. If name is None, the name of the property will be computed from the name of the callable. If the callable is a property descriptor a ValueError will be raised if name is None or reify is True. See pyramid.request.Request.set_property() for more details on property vs reify. When reify is True, the value of property is assumed to also be True. In all cases, callable may also be a dotted Python name which refers to either a callable or a property descriptor. If callable is None then the method is only used to assist in conict detection between different addons requesting the same attribute on the request object. This is the recommended method for extending the request object and should be used in favor of providing a custom request factory via pyramid.config.Configurator.set_request_factory(). New in version 1.4. set_request_property(callable, name=None, reify=False) Add a property to the request object.
This method has been docs-deprecated as of Pyramid 1.4. pyramid.config.Configurator.add_request_method() should be used instead. New in version 1.3. Using I18N 555
43. PYRAMID.CONFIG add_translation_dirs(*specs) Add one or more translation directory paths to the current conguration state. The specs argument is a sequence that may contain absolute directory paths (e.g. /usr/share/locale) or asset specication names naming a directory path (e.g. some.package:locale) or a combination of the two. Example:
config.add_translation_dirs(/usr/share/locale, some.package:locale)
Later calls to add_translation_dir insert directories into the beginning of the list of translation directories created by earlier calls. This means that the same translation found in a directory added later in the conguration process will be found before one added earlier in the conguration process. However, if multiple specs are provided in a single call to add_translation_dirs, the directories will be inserted into the beginning of the directory list in the order theyre provided in the *specs list argument (items earlier in the list trump ones later in the list). set_locale_negotiator(negotiator) Set the locale negotiator for this application. The locale negotiator is a callable which accepts a request object and which returns a locale name. The negotiator argument should be the locale negotiator implementation or a dotted Python name which refers to such an implementation. Later calls to this method override earlier calls; there can be only one locale negotiator active at a time within an application. See Activating Translation for more information.
Using the locale_negotiator argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose.
Overriding Assets override_asset(to_override, override_with, _override=None) Add a Pyramid asset override to the current conguration state.
556
to_override is a asset specication to the asset being overridden. override_with is a asset specication to the asset that is performing the override. See Static Assets for more information about asset overrides. Setting Renderer Globals set_renderer_globals_factory(factory, warn=True) The object passed as factory should be an callable (or a dotted Python name which refers to an callable) that will be used by the Pyramid rendering machinery as a renderers global factory (see Adding Renderer Globals (Deprecated)). The factory callable must accept a single argument named system (which will be a dictionary) and it must return a dictionary. When an application uses a renderer, the factorys return dictionary will be merged into the system dictionary, and therefore will be made available to the code which uses the renderer.
This method is deprecated as of Pyramid 1.1. Use a BeforeRender event subscriber as documented in the Using Hooks chapter instead.
Using the renderer_globals_factory argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose. Getting and Adding Settings add_settings(settings=None, **kw) Augment the deployment settings with one or more key/value pairs. You may pass a dictionary:
config.add_settings({external_uri:http://example.com})
557
43. PYRAMID.CONFIG
config.add_settings(external_uri=http://example.com)
This function is useful when you need to test code that accesses the pyramid.registry.Registry.settings API (or the pyramid.config.Configurator.get_settings() API) and which uses values from that API. get_settings() Return a deployment settings object for the current application. A deployment settings object is a dictionary-like object that contains key/value pairs based on the dictionary passed as the settings argument to the pyramid.config.Configurator constructor.
Hooking Pyramid Behavior add_renderer(name, factory) Add a Pyramid renderer factory to the current conguration state. The name argument is the renderer name. Use None to represent the default renderer (a renderer which will be used for all views unless they name another renderer specically). The factory argument is Python reference to an implementation of a renderer factory or a dotted Python name to same. add_resource_url_adapter(adapter, resource_iface=None) When you add a traverser as described in Changing the Traverser, its convenient to continue to use the pyramid.request.Request.resource_url() API. However, since the way traversal is done may have been modied, the URLs that resource_url generates by default may be incorrect when resources are returned by a custom traverser. If youve added a traverser, you can change how resource_url() generates a URL for a specic type of resource by calling this method. 558
The adapter argument represents a class that implements the IResourceURL interface. The class constructor should accept two arguments in its constructor (the resource and the request) and the resulting instance should provide the attributes detailed in that interface (virtual_path and physical_path, in particular). The resource_iface argument represents a class or interface that the resource should possess for this url adapter to be used when pyramid.request.Request.resource_url() looks up a resource url adapter. If resource_iface is not passed, or it is passed as None, the url adapter will be used for every type of resource. See Changing How pyramid.request.Request.resource_url() Generates a URL for more information. This API is new in Pyramid 1.3. add_response_adapter(adapter, type_or_iface) When an object of type (or interface) type_or_iface is returned from a view callable, Pyramid will use the adapter adapter to convert it into an object which implements the pyramid.interfaces.IResponse interface. If adapter is None, an object returned of type (or interface) type_or_iface will itself be used as a response object. adapter and type_or_interface may be Python objects or strings representing dotted names to importable Python global objects. See Changing How Pyramid Treats View Responses for more information. add_traverser(adapter, iface=None) The superdefault traversal algorithm that Pyramid uses is explained in The Traversal Algorithm. Though it is rarely necessary, this default algorithm can be swapped out selectively for a different traversal pattern via conguration. The section entitled Changing the Traverser details how to create a traverser class. For example, to override the superdefault traverser used by Pyramid, you might do something like this:
559
43. PYRAMID.CONFIG This would cause the Pyramid superdefault traverser to never be used; intead all traversal would be done using your MyCustomTraverser class, no matter which object was returned by the root factory of this application. Note that we passed no arguments to the iface keyword parameter. The default value of iface, None represents that the registered traverser should be used when no other more specic traverser is available for the object returned by the root factory. However, more than one traversal algorithm can be active at the same time. The traverser used can depend on the result of the root factory. For instance, if your root factory returns more than one type of object conditionally, you could claim that an alternate traverser adapter should be used agsinst one particular class or interface returned by that root factory. When the root factory returned an object that implemented that class or interface, a custom traverser would be used. Otherwise, the default traverser would be used. The iface argument represents the class of the object that the root factory might return or an interface that the object might implement. To use a particular traverser only when the root factory returns a particular class:
config.add_traverser(MyCustomTraverser, MyRootClass)
When more than one traverser is active, the most specic traverser will be used (the one that matches the class or interface of the value returned by the root factory most closely). Note that either adapter or iface can be a dotted Python name or a Python object. See Changing the Traverser for more information. add_tween(tween_factory, under=None, over=None) This feature is new as of Pyramid 1.2.
Add a tween factory. A tween (a contraction of between) is a bit of code that sits between the Pyramid routers main request handling function and the upstream WSGI component that uses Pyramid as its app. Tweens are a feature that may be used by Pyramid framework extensions, 560
to provide, for example, Pyramid-specic view timing support, bookkeeping code that examines exceptions before they are returned to the upstream WSGI application, or a variety of other features. Tweens behave a bit like WSGI middleware but they have the benet of running in a context in which they have access to the Pyramid application registry as well as the Pyramid rendering machinery.
You can view the tween ordering congured into a given Pyramid application by using the ptweens command. See Displaying Tweens. The tween_factory argument must be a dotted Python name to a global object representing the tween factory. The under and over arguments allow the caller of add_tween to provide a hint about where in the tween chain this tween factory should be placed when an implicit tween chain is used. These hints are only used when an explicit tween chain is not used (when the pyramid.tweens conguration value is not set). Allowable values for under or over (or both) are: None (the default). A dotted Python name to a tween factory: a string representing the dotted name of a tween factory added in a call to add_tween in the same conguration session. One of the constants pyramid.tweens.MAIN, pyramid.tweens.INGRESS, or pyramid.tweens.EXCVIEW. An iterable of any combination of the above. This allows the user to specify fallbacks if the desired tween is not included, as well as compatibility with multiple other tweens. under means closer to the main Pyramid application than, over means closer to the request ingress than. For example, calling add_tween(myapp.tfactory, over=pyramid.tweens.MAIN) will attempt to place the tween factory represented by the dotted name myapp.tfactory directly above (in ptweens order) the main Pyramid request handler. Likewise, calling add_tween(myapp.tfactory, over=pyramid.tweens.MAIN, under=mypkg.someothertween) will attempt to place this tween factory above the main handler but below (a ctional) mypkg.someothertween tween factory. If all options for under (or over) cannot be found in the current conguration, it is an error. If some options are specied purely 561
43. PYRAMID.CONFIG for compatibilty with other tweens, just add a fallback of MAIN or INGRESS. For example, under=(mypkg.someothertween, mypkg.someothertween2, INGRESS). This constraint will require the tween to be located under both the mypkg.someothertween tween, the mypkg.someothertween2 tween, and INGRESS. If any of these is not in the current conguration, this constraint will only organize itself based on the tweens that are present. Specifying neither over nor under is equivalent to specifying under=INGRESS. Implicit tween ordering is obviously only best-effort. Pyramid will attempt to present an implicit order of tweens as best it can, but the only surere way to get any particular ordering is to use an explicit tween order. A user may always override the implicit tween ordering by using an explicit pyramid.tweens conguration value setting. under, and over arguments are ignored when an explicit tween chain is specied using the pyramid.tweens conguration value. For more information, see Registering Tweens. add_route_predicate(name, factory, weighs_more_than=None, weighs_less_than=None) Adds a route predicate factory. The view predicate can later be named as a keyword argument to pyramid.config.Configurator.add_route(). name should be the name of the predicate. It must be a valid Python identier (it will be used as a keyword argument to add_view). factory should be a predicate factory. See View and Route Predicates for more information.
add_view_predicate(name, factory, weighs_more_than=None, weighs_less_than=None) Adds a view predicate factory. The associated view predicate can later be named as a keyword argument to 562
in
the
name should be the name of the predicate. It must be a valid Python identier (it will be used as a keyword argument to add_view by others). factory should be a predicate factory. See View and Route Predicates for more information.
set_request_factory(factory) The object passed as factory should be an object (or a dotted Python name which refers to an object) which will be used by the Pyramid router to create all request objects. This factory object must have the same methods and attributes as the pyramid.request.Request class (particularly __call__, and blank). See pyramid.config.Configurator.add_request_method() for a less intrusive way to extend the request objects with custom methods and properties.
Using the request_factory argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose.
set_root_factory(factory) Add a root factory to the current conguration state. If the factory argument is None a default root factory will be registered.
Using the root_factory argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose.
563
43. PYRAMID.CONFIG set_session_factory(factory) Congure the application with a session factory. If this method is called, the factory argument must be a session factory callable or a dotted Python name to that factory.
Using the session_factory argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose.
set_view_mapper(mapper) Setting a view mapper makes it possible to make use of view callable objects which implement different call signatures than the ones supported by Pyramid as described in its narrative documentation. The mapper should argument be an object implementing pyramid.interfaces.IViewMapperFactory or a dotted Python name to such an object. The provided mapper will become the default view mapper to be used by all subsequent view conguration registrations. See also Using a View Mapper.
Using the default_view_mapper argument to the pyramid.config.Configurator constructor can be used to achieve the same purpose.
Extension Author APIs action(discriminator, callable=None, args=(), kw=None, order=0, introspectables=(), **extra) Register an action which will be executed when pyramid.config.Configurator.commit() is called (or executed immediately if autocommit is True).
This method is typically only used by Pyramid framework extension authors, not by Pyramid application developers.
564
The discriminator uniquely identies the action. It must be given, but it can be None, to indicate that the action never conicts. It must be a hashable value. The callable is a callable object which performs the task associated with the action when the action is executed. It is optional. args and kw are tuple and dict objects respectively, which are passed to callable when this action is executed. Both are optional. order is a grouping mechanism; an action with a lower order will be executed before an action with a higher order (has no effect when autocommit is True). introspectables is a sequence of introspectable objects (or the empty sequence if no introspectable objects are associated with this action). If this congurators introspection attribute is False, these introspectables will be ignored. extra provides a facility for inserting extra keys and values into an action dictionary. add_directive(name, directive, action_wrap=True) Add a directive method to the congurator. This method is typically only used by Pyramid framework extension authors, not by Pyramid application developers. Framework extenders can add directive methods to a congurator by instructing their users to call config.add_directive(somename, some.callable). This will make some.callable accessible as config.somename. some.callable should be a function which accepts config as a rst argument, and arbitrary positional and keyword arguments following. It should use cong.action as necessary to perform actions. Directive methods can then be invoked like built-in directives such as add_view, add_route, etc. The action_wrap argument should be True for directives which perform config.action with potentially conicting discriminators. action_wrap will cause the directive to be wrapped in a decorator which provides more accurate conict cause information. add_directive does not participate in conict detection, and later calls to add_directive will override earlier calls. 565
43. PYRAMID.CONFIG with_package(package) Return a new Congurator instance with the same registry as this congurator using the package supplied as the package argument to the new congurator. package may be an actual Python package object or a dotted Python name representing a package. derive_view(view, attr=None, renderer=None) Create a view callable using the function, instance, or class (or dotted Python name referring to the same) provided as view object.
This method is typically only used by Pyramid framework extension authors, not by Pyramid application developers.
This is API is useful to framework extenders who create pluggable systems which need to register proxy view callables for functions, instances, or classes which meet the requirements of being a Pyramid view callable. For example, a some_other_framework function in another framework may want to allow a user to supply a view callable, but he may want to wrap the view callable in his own before registering the wrapper as a Pyramid view callable. Because a Pyramid view callable can be any of a number of valid objects, the framework extender will not know how to call the user-supplied object. Running it through derive_view normalizes it to a callable which accepts two arguments: context and request. For example:
def some_other_framework(user_supplied_view): config = Configurator(reg) proxy_view = config.derive_view(user_supplied_view) def my_wrapper(context, request): do_something_that_mutates(request) return proxy_view(context, request) config.add_view(my_wrapper)
The view object provided should be one of the following: A function or another non-class callable object that accepts a request as a single positional argument and which returns a response object. A function or other non-class callable object that accepts two positional arguments, context, request and which returns a response object. 566
A class which accepts a single positional argument in its constructor named request, and which has a __call__ method that accepts no arguments that returns a response object. A class which accepts two positional arguments named context, request, and which has a __call__ method that accepts no arguments that returns a response object. A dotted Python name which refers to any of the kinds of objects above. This API returns a callable which accepts the arguments context, request and which returns the result of calling the provided view object. The attr keyword argument is most useful when the view object is a class. It names the method that should be used as the callable. If attr is not provided, the attribute effectively defaults to __call__. See Dening a View Callable as a Class for more information. The renderer keyword argument should be a renderer name. If supplied, it will cause the returned callable to use a renderer to convert the user-supplied view result to a response object. If a renderer argument is not supplied, the user-supplied view must itself return a response object. Utility Methods absolute_asset_spec(relative_spec) Resolve the potentially relative asset specication string passed as relative_spec into an absolute asset specication string and return the string. Use the package of this congurator as the package to which the asset specication will be considered relative when generating an absolute asset specication. If the provided relative_spec argument is already absolute, or if the relative_spec is not a string, it is simply returned. maybe_dotted(dotted) Resolve the dotted Python name dotted to a global Python object. If dotted is not a string, return it without attempting to do any name resolution. If dotted is a relative dotted name (e.g. .foo.bar, consider it relative to the package argument supplied to this Congurators constructor. ZCA-Related APIs
567
43. PYRAMID.CONFIG hook_zca() Call zope.component.getSiteManager.sethook() with the argument pyramid.threadlocal.get_current_registry, causing the Zope Component Architecture global APIs such as zope.component.getSiteManager(), zope.component.getAdapter() and others to use the Pyramid application registry rather than the Zope global registry. unhook_zca() Call zope.component.getSiteManager.reset() to undo the action of pyramid.config.Configurator.hook_zca(). setup_registry(settings=None, root_factory=None, authentication_policy=None, authorization_policy=None, renderers=None, debug_logger=None, locale_negotiator=None, request_factory=None, renderer_globals_factory=None, default_permission=None, session_factory=None, default_view_mapper=None, exceptionresponse_view=<function default_exceptionresponse_view at 0x428f230>) When you pass a non-None registry argument to the Congurator constructor, no initial setup is performed against the registry. This is because the registry you pass in may have already been initialized for use under Pyramid via a different congurator. However, in some circumstances (such as when you want to use a global registry instead of a registry created as a result of the Congurator constructor), or when you want to reset the initial setup of a registry, you do want to explicitly initialize the registry associated with a Congurator for use under Pyramid. Use setup_registry to do this initialization. setup_registry congures settings, a root factory, security policies, renderers, a debug logger, a locale negotiator, and various other settings using the congurators current registry, as per the descriptions in the Congurator constructor. Testing Helper APIs testing_add_renderer(path, renderer=None) Unit/integration testing helper: register a renderer at path (usually a relative lename ala templates/foo.pt or an asset specication) and return the renderer object. If the renderer argument is None, a dummy renderer will be used. This function is useful when testing code that calls the pyramid.renderers.render() function or 568
pyramid.renderers.render_to_response() function or any other render_* or get_* API of the pyramid.renderers module. Note that calling this method for with a path argument representing a renderer factory type (e.g. for foo.pt usually implies the chameleon_zpt renderer factory) clobbers any existing renderer factory registered for that type.
This method is also available under testing_add_template (an older name for it).
the
alias
testing_add_subscriber(event_iface=None) Unit/integration testing helper: Registers a subscriber which listens for events of the type event_iface. This method returns a list object which is appended to by the subscriber whenever an event is captured. When an event is dispatched that matches the value implied by the event_iface argument, that event will be appended to the list. You can then compare the values in the list to expected event notications. This method is useful when testing code that wants to call pyramid.registry.Registry.notify(), or zope.component.event.dispatch(). The default value of event_iface (None) implies a subscriber registered for any kind of event. testing_resources(resources) Unit/integration testing helper: registers a dictionary of resource objects that can be resolved via the pyramid.traversal.find_resource() API. The pyramid.traversal.find_resource() API is called with a path as one of its arguments. If the dictionary you register when calling this method contains that path as a string key (e.g. /foo/bar or foo/bar), the corresponding value will be returned to find_resource (and thus to your code) when pyramid.traversal.find_resource() is called with an equivalent path string or tuple. testing_securitypolicy(userid=None, groupids=(), permissive=True, remember_result=None, forget_result=None) Unit/integration testing helper: Registers a pair of faux Pyramid security policies: a authentication policy and a authorization policy. 569
43. PYRAMID.CONFIG The behavior of the registered authorization policy depends on the permissive argument. If permissive is true, a permissive authorization policy is registered; this policy allows all access. If permissive is false, a nonpermissive authorization policy is registered; this policy denies all access. remember_result, if provided, should be the result returned by the remember method of the faux authentication policy. If it is not provided (or it is provided, and is None), the default value [] (the empty list) will be returned by remember. remember_result is new as of Pyramid 1.4. forget_result, if provided, should be the result returned by the forget method of the faux authentication policy. If it is not provided (or it is provided, and is None), the default value [] (the empty list) will be returned by forget. forget_result is new as of Pyramid 1.4. The behavior of the registered authentication policy depends on the values provided for the userid and groupids argument. The authentication policy will return the userid identier implied by the userid argument and the group ids implied by the groupids argument when the pyramid.security.authenticated_userid() or pyramid.security.effective_principals() APIs are used. This function is most useful when testing code that uses the APIs named pyramid.security.has_permission(), pyramid.security.authenticated_userid(), pyramid.security.effective_principals(), and pyramid.security.principals_allowed_by_permission(). Attributes introspectable A shortcut attribute which points to the pyramid.registry.Introspectable class (used during directives to provide introspection to actions). This attribute is new as of Pyramid 1.3.
570
introspector The introspector related to this conguration. It is an instance implementing the pyramid.interfaces.IIntrospector interface.
registry The application registry which holds the conguration associated with this congurator. global_registries The set of registries that have been created for Pyramid applications, one per each call to pyramid.config.Configurator.make_wsgi_app() in the current process. The object itself supports iteration and has a last property containing the last registry loaded. The registries contained in this object are stored as weakrefs, thus they will only exist for the lifetime of the actual applications for which they are being used.
571
43. PYRAMID.CONFIG
572
CHAPTER
FORTYFOUR
PYRAMID.EVENTS
44.1 Functions
subscriber(*ifaces, **predicates) Decorator activated via a scan which treats the function being decorated as an event subscriber for the set of interfaces passed as *ifaces and the set of predicate terms passed as **predicates to the decorator constructor. For example:
from pyramid.events import NewRequest from pyramid.events import subscriber @subscriber(NewRequest) def mysubscriber(event): event.request.foo = 1
More than one event type can be passed as a constructor argument. The decorated subscriber will be called for each event type.
from pyramid.events import NewRequest, NewResponse from pyramid.events import subscriber @subscriber(NewRequest, NewResponse) def mysubscriber(event): print event
When the subscriber decorator is used without passing an arguments, the function it decorates is called for every event sent: 573
44. PYRAMID.EVENTS
This method will have no effect until a scan is performed against the package or module which contains it, ala:
from pyramid.config import Configurator config = Configurator() config.scan(somepackage_containing_subscribers)
Any arguments will be passed along to **predicate pyramid.config.Configurator.add_subscriber(). See Subscriber Predicates for a description of how predicates can narrow the set of circumstances in which a subscriber will be called.
For backwards compatibility purposes, this class can also be imported as pyramid.events.WSGIApplicationCreatedEvent. This was the name of the event class before Pyramid 1.0.
class NewRequest(request) An instance of this class is emitted as an event whenever Pyramid begins to process a new request. The event instance has an attribute, request, which is a request object. This event class implements the pyramid.interfaces.INewRequest interface.
574
44.2. EVENT TYPES class ContextFound(request) An instance of this class is emitted as an event after the Pyramid router nds a context object (after it performs traversal) but before any view code is executed. The instance has an attribute, request, which is the request object generated by Pyramid. Notably, the request object will have an attribute named context, which is the context that will be provided to the view which will eventually be called, as well as other attributes attached by context-nding code. This class implements the pyramid.interfaces.IContextFound interface.
As of Pyramid 1.0, for backwards compatibility purposes, this event may also be imported as pyramid.events.AfterTraversal. class NewResponse(request, response) An instance of this class is emitted as an event whenever any Pyramid view or exception view returns a response. The instance has two attributes:request, which is the request which caused the response, and response, which is the response object returned by a view or renderer. If the response was generated by an exception view, the request will have an attribute named exception, which is the exception object which caused the exception view to be executed. If the response was generated by a normal view, this attribute of the request will be None. This event will not be generated if a response cannot be created due to an exception that is not caught by an exception view (no response is created under this circumstace). This class implements the pyramid.interfaces.INewResponse interface.
Postprocessing a response is usually better handled in a WSGI middleware component than in subscriber code that is called by a pyramid.interfaces.INewResponse event. The pyramid.interfaces.INewResponse event exists almost purely for symmetry with the pyramid.interfaces.INewRequest event. class BeforeRender(system, rendering_val=None) Subscribers to this event may introspect and modify the set of renderer globals before they are passed to a renderer. This event object iself has a dictionary-like interface that can be used for this purpose. For example:
575
44. PYRAMID.EVENTS
from pyramid.events import subscriber from pyramid.events import BeforeRender @subscriber(BeforeRender) def add_global(event): event[mykey] = foo
An object of this type is sent as an event just before a renderer is invoked (but after the deprecated application-level renderer globals factory added via pyramid.config.Configurator.set_renderer_globals_factory, if any, has injected its own keys into the renderer globals dictionary). If a subscriber adds a key via __setitem__ that already exists in the renderer globals dictionary, it will overwrite the older value there. This can be problematic because event subscribers to the BeforeRender event do not possess any relative ordering. For maximum interoperability with other third-party subscribers, if you write an event subscriber meant to be used as a BeforeRender subscriber, your subscriber code will need to ensure no value already exists in the renderer globals dictionary before setting an overriding value (which can be done using .get or __contains__ of the event object). The dictionary returned from the view is accessible through the rendering_val attribute of a BeforeRender event. Suppose you return {mykey: your view callable, like so: somevalue, mykey2: somevalue2} from
from pyramid.view import view_config @view_config(renderer=some_renderer) def myview(request): return {mykey: somevalue, mykey2: somevalue2}
rendering_val can be used to access these values from the BeforeRender object:
from pyramid.events import subscriber from pyramid.events import BeforeRender @subscriber(BeforeRender) def read_return(event): # {mykey: somevalue} is returned from the view print(event.rendering_val[mykey])
576
44.2. EVENT TYPES In other words, rendering_val is the (non-system) value returned by a view or passed to render* as value. This feature is new in Pyramid 1.2. For a description of the values present in the renderer globals dictionary, see System Values Used During Rendering. See also pyramid.interfaces.IBeforeRender. update(E, **F ) Update D from dict/iterable E and F. If E has a .keys() method, does: for k in E: D[k] = E[k] If E lacks .keys() method, does: for (k, v) in E: D[k] = v. In either case, this is followed by: for k in F: D[k] = F[k]. clear() None. Remove all items from D. copy() a shallow copy of D static fromkeys(S[, v ]) New dict with keys from S and values equal to v. v defaults to None. get(k[, d ]) D[k] if k in D, else d. d defaults to None. has_key(k) True if D has a key k, else False items() list of Ds (key, value) pairs, as 2-tuples iteritems() an iterator over the (key, value) items of D iterkeys() an iterator over the keys of D itervalues() an iterator over the values of D keys() list of Ds keys pop(k[, d ]) v, remove specied key and return the corresponding value. If key is not found, d is returned if given, otherwise KeyError is raised popitem() (k, v), remove and return some (key, value) pair as a 2-tuple; but raise KeyError if D is empty. setdefault(k[, d ]) D.get(k,d), also set D[k]=d if k not in D values() list of Ds values viewitems() a set-like object providing a view on Ds items viewkeys() a set-like object providing a view on Ds keys viewvalues() an object providing a view on Ds values See Using Events for more information about how to register code which subscribes to these events. 577
44. PYRAMID.EVENTS
578
CHAPTER
FORTYFIVE
PYRAMID.EXCEPTIONS
Forbidden alias of HTTPForbidden NotFound alias of HTTPNotFound class ConfigurationError Raised when inappropriate input values are supplied to an API method of a Congurator class URLDecodeError This exception is raised when Pyramid cannot successfully decode a URL or a URL path segment. This exception it behaves just like the Python builtin UnicodeDecodeError. It is a subclass of the builtin UnicodeDecodeError exception only for identity purposes, mostly so an exception view can be registered when a URL cannot be decoded.
579
45. PYRAMID.EXCEPTIONS
580
CHAPTER
FORTYSIX
PYRAMID.HTTPEXCEPTIONS
46.1 HTTP Exceptions
This module contains Pyramid HTTP exception classes. Each class relates to a single HTTP status code. Each class is a subclass of the HTTPException. Each exception class is also a response object. Each exception class has a status code according to RFC 2068: codes with 100-300 are not really errors; 400s are client errors, and 500s are server errors. Exception HTTPException HTTPOk 200 - HTTPOk 201 - HTTPCreated 202 - HTTPAccepted 203 - HTTPNonAuthoritativeInformation 204 - HTTPNoContent 205 - HTTPResetContent 581
46. PYRAMID.HTTPEXCEPTIONS 206 - HTTPPartialContent HTTPRedirection 300 - HTTPMultipleChoices 301 - HTTPMovedPermanently 302 - HTTPFound 303 - HTTPSeeOther 304 - HTTPNotModied 305 - HTTPUseProxy 306 - Unused (not implemented, obviously) 307 - HTTPTemporaryRedirect HTTPError HTTPClientError 400 - HTTPBadRequest 401 - HTTPUnauthorized 402 - HTTPPaymentRequired 403 - HTTPForbidden 404 - HTTPNotFound 405 - HTTPMethodNotAllowed 406 - HTTPNotAcceptable 407 - HTTPProxyAuthenticationRequired 408 - HTTPRequestTimeout 409 - HTTPConict 582
46.1. HTTP EXCEPTIONS 410 - HTTPGone 411 - HTTPLengthRequired 412 - HTTPPreconditionFailed 413 - HTTPRequestEntityTooLarge 414 - HTTPRequestURITooLong 415 - HTTPUnsupportedMediaType 416 - HTTPRequestRangeNotSatisable 417 - HTTPExpectationFailed 422 - HTTPUnprocessableEntity 423 - HTTPLocked 424 - HTTPFailedDependency HTTPServerError 500 - HTTPInternalServerError 501 - HTTPNotImplemented 502 - HTTPBadGateway 503 - HTTPServiceUnavailable 504 - HTTPGatewayTimeout 505 - HTTPVersionNotSupported 507 - HTTPInsufcientStorage HTTP exceptions are also response objects, thus they accept most of the same parameters that can be passed to a regular Response. Each HTTP exception also has the following attributes: code the HTTP status code for the exception title remainder of the status line (stuff after the code) 583
46. PYRAMID.HTTPEXCEPTIONS explanation a plain-text explanation of the error message that is not subject to environment or header substitutions; it is accessible in the template via ${explanation} detail a plain-text message customization that is not subject to environment or header substitutions; accessible in the template via ${detail} body_template a String.template-format content fragment used for environment and header substitution; the default template includes both the explanation and further detail provided in the message. Each HTTP exception accepts the following parameters, any others will be forwarded to its Response superclass: detail a plain-text override of the default detail headers a list of (k,v) header pairs comment a plain-text additional information which is usually stripped/hidden for end-users body_template a string.Template object containing a content fragment in HTML that frames the explanation and further detail body a string that will override the body_template and be used as the body of the response. Substitution of response headers into template values is always performed. Substitution of WSGI environment values is performed if a request is passed to the exceptions constructor. The subclasses of _HTTPMove (HTTPMultipleChoices, HTTPMovedPermanently, HTTPFound, HTTPSeeOther, HTTPUseProxy and HTTPTemporaryRedirect) are redirections that require a Location eld. Reecting this, these subclasses have one additional keyword argument: location, which indicates the location to which to redirect. status_map A mapping of integer status code to exception class (eg. pyramid.httpexceptions.HTTPUnauthorized). exception_response(status_code, **kw) Creates an HTTP exception based on a status code. Example:
raise exception_response(404) # raises an HTTPNotFound exception.
46.1. HTTP EXCEPTIONS class HTTPException(detail=None, headers=None, comment=None, body_template=None, **kw) class HTTPOk(detail=None, headers=None, comment=None, body_template=None, **kw) Base class for exceptions with status codes in the 200s (successful responses) code: 200, title: OK class HTTPRedirection(detail=None, headers=None, body_template=None, **kw) base class for exceptions with status codes in the 300s (redirections) comment=None,
This is an abstract base class for 3xx redirection. It indicates that further action needs to be taken by the user agent in order to fulll the request. It does not necessarly signal an error condition. class HTTPError(detail=None, headers=None, comment=None, body_template=None, **kw) base class for exceptions with status codes in the 400s and 500s This is an exception which indicates that an error has occurred, and that any work in progress should not be committed. class HTTPClientError(detail=None, headers=None, body_template=None, **kw) base class for the 400s, where the client is in error comment=None,
This is an error condition in which the client is presumed to be in-error. This is an expected problem, and thus is not considered a bug. A server-side traceback is not warranted. Unless specialized, this is a 400 Bad Request class HTTPServerError(detail=None, headers=None, body_template=None, **kw) base class for the 500s, where the server is in-error comment=None,
This is an error condition in which the server is presumed to be in-error. Unless specialized, this is a 500 Internal Server Error. class HTTPCreated(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPOk This indicates that request has been fullled and resulted in a new resource being created. code: 201, title: Created
585
46. PYRAMID.HTTPEXCEPTIONS class HTTPAccepted(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPOk This indicates that the request has been accepted for processing, but the processing has not been completed. code: 202, title: Accepted class HTTPNonAuthoritativeInformation(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPOk This indicates that the returned metainformation in the entity-header is not the denitive set as available from the origin server, but is gathered from a local or a third-party copy. code: 203, title: Non-Authoritative Information class HTTPNoContent(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPOk This indicates that the server has fullled the request but does not need to return an entity-body, and might want to return updated metainformation. code: 204, title: No Content class HTTPResetContent(detail=None, headers=None, body_template=None, **kw) subclass of HTTPOk comment=None,
This indicates that the server has fullled the request and the user agent SHOULD reset the document view which caused the request to be sent. code: 205, title: Reset Content class HTTPPartialContent(detail=None, headers=None, body_template=None, **kw) subclass of HTTPOk comment=None,
This indicates that the server has fullled the partial GET request for the resource. code: 206, title: Partial Content
586
46.1. HTTP EXCEPTIONS class HTTPMultipleChoices(location=, detail=None, headers=None, comment=None, body_template=None, **kw) subclass of _HTTPMove This indicates that the requested resource corresponds to any one of a set of representations, each with its own specic location, and agent-driven negotiation information is being provided so that the user can select a preferred representation and redirect its request to that location. code: 300, title: Multiple Choices class HTTPMovedPermanently(location=, detail=None, headers=None, comment=None, body_template=None, **kw) subclass of _HTTPMove This indicates that the requested resource has been assigned a new permanent URI and any future references to this resource SHOULD use one of the returned URIs. code: 301, title: Moved Permanently class HTTPFound(location=, detail=None, body_template=None, **kw) subclass of _HTTPMove headers=None, comment=None,
This indicates that the requested resource resides temporarily under a different URI. code: 302, title: Found class HTTPSeeOther(location=, detail=None, body_template=None, **kw) subclass of _HTTPMove headers=None, comment=None,
This indicates that the response to the request can be found under a different URI and SHOULD be retrieved using a GET method on that resource. code: 303, title: See Other class HTTPNotModified(detail=None, headers=None, body_template=None, **kw) subclass of HTTPRedirection comment=None,
This indicates that if the client has performed a conditional GET request and access is allowed, but the document has not been modied, the server SHOULD respond with this status code. code: 304, title: Not Modied 587
46. PYRAMID.HTTPEXCEPTIONS class HTTPUseProxy(location=, detail=None, body_template=None, **kw) subclass of _HTTPMove headers=None, comment=None,
This indicates that the requested resource MUST be accessed through the proxy given by the Location eld. code: 305, title: Use Proxy class HTTPTemporaryRedirect(location=, detail=None, headers=None, comment=None, body_template=None, **kw) subclass of _HTTPMove This indicates that the requested resource resides temporarily under a different URI. code: 307, title: Temporary Redirect class HTTPBadRequest(detail=None, headers=None, comment=None, body_template=None, **kw) class HTTPUnauthorized(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError This indicates that the request requires user authentication. code: 401, title: Unauthorized class HTTPPaymentRequired(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError code: 402, title: Payment Required class HTTPForbidden(detail=None, headers=None, comment=None, body_template=None, result=None, **kw) subclass of HTTPClientError This indicates that the server understood the request, but is refusing to fulll it. code: 403, title: Forbidden Raise this exception within view code to immediately return the forbidden view to the invoking user. Usually this is a basic 403 page, but the forbidden view can be customized as necessary. See 588 comment=None, comment=None,
46.1. HTTP EXCEPTIONS Changing the Forbidden View. A Forbidden exception will be the context of a Forbidden View. This exceptions constructor treats two arguments specially. The rst argument, detail, should be a string. The value of this string will be used as the message attribute of the exception object. The second special keyword argument, result is usually an instance of pyramid.security.Denied or pyramid.security.ACLDenied each of which indicates a reason for the forbidden error. However, result is also permitted to be just a plain boolean False object or None. The result value will be used as the result attribute of the exception object. It defaults to None. The Forbidden View can use the attributes of a Forbidden exception as necessary to provide extended information in an error report shown to a user. class HTTPNotFound(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPClientError This indicates that the server did not nd anything matching the Request-URI. code: 404, title: Not Found Raise this exception within view code to immediately return the Not Found view to the invoking user. Usually this is a basic 404 page, but the Not Found view can be customized as necessary. See Changing the Not Found View. This exceptions constructor accepts a detail argument (the rst argument), which should be a string. The value of this string will be available as the message attribute of this exception, for availability to the Not Found View. class HTTPMethodNotAllowed(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the method specied in the Request-Line is not allowed for the resource identied by the Request-URI. code: 405, title: Method Not Allowed class HTTPNotAcceptable(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates the resource identied by the request is only capable of generating response entities which have content characteristics not acceptable according to the accept headers sent in the request. code: 406, title: Not Acceptable 589
46. PYRAMID.HTTPEXCEPTIONS class HTTPProxyAuthenticationRequired(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPClientError This is similar to 401, but indicates that the client must rst authenticate itself with the proxy. code: 407, title: Proxy Authentication Required class HTTPRequestTimeout(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the client did not produce a request within the time that the server was prepared to wait. code: 408, title: Request Timeout class HTTPConflict(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPClientError This indicates that the request could not be completed due to a conict with the current state of the resource. code: 409, title: Conict class HTTPGone(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPClientError This indicates that the requested resource is no longer available at the server and no forwarding address is known. code: 410, title: Gone class HTTPLengthRequired(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the server refuses to accept the request without a dened Content-Length. code: 411, title: Length Required
590
46.1. HTTP EXCEPTIONS class HTTPPreconditionFailed(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the precondition given in one or more of the request-header elds evaluated to false when it was tested on the server. code: 412, title: Precondition Failed class HTTPRequestEntityTooLarge(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the server is refusing to process a request because the request entity is larger than the server is willing or able to process. code: 413, title: Request Entity Too Large class HTTPRequestURITooLong(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the server is refusing to service the request because the Request-URI is longer than the server is willing to interpret. code: 414, title: Request-URI Too Long class HTTPUnsupportedMediaType(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the server is refusing to service the request because the entity of the request is in a format not supported by the requested resource for the requested method. code: 415, title: Unsupported Media Type class HTTPRequestRangeNotSatisfiable(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPClientError The server SHOULD return a response with this status code if a request included a Range requestheader eld, and none of the range-specier values in this eld overlap the current extent of the selected resource, and the request did not include an If-Range request-header eld. code: 416, title: Request Range Not Satisable 591
46. PYRAMID.HTTPEXCEPTIONS class HTTPExpectationFailed(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indidcates that the expectation given in an Expect request-header eld could not be met by this server. code: 417, title: Expectation Failed class HTTPUnprocessableEntity(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the server is unable to process the contained instructions. Only for WebDAV. code: 422, title: Unprocessable Entity class HTTPLocked(detail=None, headers=None, **kw) subclass of HTTPClientError comment=None, body_template=None,
This indicates that the resource is locked. Only for WebDAV code: 423, title: Locked class HTTPFailedDependency(detail=None, headers=None, body_template=None, **kw) subclass of HTTPClientError comment=None,
This indicates that the method could not be performed because the requested action depended on another action and that action failed. Only for WebDAV. code: 424, title: Failed Dependency class HTTPInternalServerError(detail=None, headers=None, body_template=None, **kw) class HTTPNotImplemented(detail=None, headers=None, body_template=None, **kw) subclass of HTTPServerError comment=None,
comment=None,
This indicates that the server does not support the functionality required to fulll the request. code: 501, title: Not Implemented 592
46.1. HTTP EXCEPTIONS class HTTPBadGateway(detail=None, headers=None, comment=None, body_template=None, **kw) subclass of HTTPServerError This indicates that the server, while acting as a gateway or proxy, received an invalid response from the upstream server it accessed in attempting to fulll the request. code: 502, title: Bad Gateway class HTTPServiceUnavailable(detail=None, headers=None, body_template=None, **kw) subclass of HTTPServerError comment=None,
This indicates that the server is currently unable to handle the request due to a temporary overloading or maintenance of the server. code: 503, title: Service Unavailable class HTTPGatewayTimeout(detail=None, headers=None, body_template=None, **kw) subclass of HTTPServerError comment=None,
This indicates that the server, while acting as a gateway or proxy, did not receive a timely response from the upstream server specied by the URI (e.g. HTTP, FTP, LDAP) or some other auxiliary server (e.g. DNS) it needed to access in attempting to complete the request. code: 504, title: Gateway Timeout class HTTPVersionNotSupported(detail=None, headers=None, body_template=None, **kw) subclass of HTTPServerError comment=None,
This indicates that the server does not support, or refuses to support, the HTTP protocol version that was used in the request message. code: 505, title: HTTP Version Not Supported class HTTPInsufficientStorage(detail=None, headers=None, body_template=None, **kw) subclass of HTTPServerError comment=None,
This indicates that the server does not have enough space to save the resource. code: 507, title: Insufcient Storage 593
46. PYRAMID.HTTPEXCEPTIONS
594
CHAPTER
FORTYSEVEN
PYRAMID.I18N
class TranslationString The constructor for a translation string. A translation string is a Unicode-like object that has some extra metadata. This constructor accepts one required argument named msgid. msgid must be the message identier for the translation string. It must be a unicode object or a str object encoded in the default system encoding. Optional keyword arguments to this objects constructor include domain, default, and mapping. domain represents the translation domain. By default, the translation domain is None, indicating that this translation string is associated with the default translation domain (usually messages). default represents an explicit default text for this translation string. Default text appears when the translation string cannot be translated. Usually, the msgid of a translation string serves double duty as as its default text. However, using this option you can provide a different default text for this translation string. This feature is useful when the default of a translation string is too complicated or too long to be used as a message identier. If default is provided, it must be a unicode object or a str object encoded in the default system encoding (usually means ASCII). If default is None (its default value), the msgid value used by this translation string will be assumed to be the value of default. mapping, if supplied, must be a dictionary-like object which represents the replacement values for any translation string replacement marker instances found within the msgid (or default) value of this translation string. 595
47. PYRAMID.I18N After a translation string is constructed, it behaves like most other unicode objects; its msgid value will be displayed when it is treated like a unicode object. Only when its ugettext method is called will it be translated. Its default value is available as the default attribute of the object, its translation domain is available as the domain attribute, and the mapping is available as the mapping attribute. The object otherwise behaves much like a Unicode string. class TranslationStringFactory Create a factory which will generate translation strings without requiring that each call to the factory be passed a domain value. A single argument is passed to this class constructor: domain. This value will be used as the domain values of translationstring.TranslationString objects generated by the __call__ of this class. The msgid, mapping, and default values provided to the __call__ method of an instance of this class have the meaning as described by the constructor of the translationstring.TranslationString class Localizer(locale_name, translations) An object providing translation and pluralizations related to the current requests locale name. A pyramid.i18n.Localizer object is created using the pyramid.i18n.get_localizer() function. locale_name The locale name for this localizer (e.g. en or en_US). pluralize(singular, plural, n, domain=None, mapping=None) Return a Unicode string translation by using two message identier objects as a singular/plural pair and an n value representing the number that appears in the message using gettext plural forms support. The singular and plural objects passed may be translation strings or unicode strings. n represents the number of elements. domain is the translation domain to use to do the pluralization, and mapping is the interpolation mapping that should be used on the result. Note that if the objects passed are translation strings, their domains and mappings are ignored. The domain and mapping arguments must be used instead. If the domain is not supplied, a default domain is used (usually messages). Example:
num = 1 translated = localizer.pluralize(Add ${num} item, Add ${num} items, num, mapping={num:num})
596
translate(tstring, domain=None, mapping=None) Translate a translation string to the current language and interpolate any replacement markers in the result. The translate method accepts three arguments: tstring (required), domain (optional) and mapping (optional). When called, it will translate the tstring translation string to a unicode object using the current locale. If the current locale could not be determined, the result of interpolation of the default value is returned. The optional domain argument can be used to specify or override the domain of the tstring (useful when tstring is a normal string rather than a translation string). The optional mapping argument can specify or override the tstring interpolation mapping, useful when the tstring argument is a simple string instead of a translation string. Example:
from pyramid.18n import TranslationString ts = TranslationString(Add ${item}, domain=mypackage, mapping={item:Item}) translated = localizer.translate(ts)
Example:
translated = localizer.translate(Add ${item}, domain=mypackage, mapping={item:Item})
get_localizer(request) Retrieve a pyramid.i18n.Localizer object corresponding to the current requests locale name. negotiate_locale_name(request) Negotiate and return the locale name associated with the current request (never cached). get_locale_name(request) Return the locale name associated with the current request (possibly cached). default_locale_negotiator(request) The default locale negotiator. Returns a locale name or None. First, the negotiator looks for the _LOCALE_ attribute of the request object (possibly set by a view or a listener for an event). Then it looks for the request.params[_LOCALE_] value. Then it looks for the request.cookies[_LOCALE_] value. 597
47. PYRAMID.I18N Finally, the negotiator returns None if the locale could not be determined via any of the previous checks (when a locale negotiator returns None, it signies that the default locale name should be used.) make_localizer(current_locale_name, translation_directories) Create a pyramid.i18n.Localizer object corresponding to the provided locale name from the translations found in the list of translation directories. See Internationalization and Localization for more information about using Pyramid internationalization and localization services within an application.
598
CHAPTER
FORTYEIGHT
PYRAMID.INTERFACES
48.1 Event-Related Interfaces
interface IApplicationCreated Event issued when the pyramid.config.Configurator.make_wsgi_app() method is called. See the documentation attached to pyramid.events.ApplicationCreated for more information.
For backwards compatibility with Pyramid versions before 1.0, this interface can also be imported as pyramid.interfaces.IWSGIApplicationCreatedEvent.
app Created application interface INewRequest An event type that is emitted whenever Pyramid begins to process a new request. See the documentation attached to pyramid.events.NewRequest for more information.
599
48. PYRAMID.INTERFACES interface IContextFound An event type that is emitted after Pyramid nds a context object but before it calls any view code. See the documentation attached to pyramid.events.ContextFound for more information.
For backwards compatibility with versions of Pyramid before 1.0, this event interface can also be imported as pyramid.interfaces.IAfterTraversal.
request The request object interface INewResponse An event type that is emitted whenever any Pyramid view returns a response. See the documentation attached to pyramid.events.NewResponse for more information. request The request object response The response object interface IBeforeRender Extends: pyramid.interfaces.IDict Subscribers to this event may introspect and modify the set of renderer globals before they are passed to a renderer. The event object itself provides a dictionary-like interface for adding and removing renderer globals. The keys and values of the dictionary are those globals. For example:
from repoze.events import subscriber from pyramid.interfaces import IBeforeRender @subscriber(IBeforeRender) def add_global(event): event[mykey] = foo
See also Using The Before Render Event. rendering_val The value returned by a view or passed to a render method for this rendering. This feature is new in Pyramid 1.2. 600
48. PYRAMID.INTERFACES permits(context, principals, permission) Return True if any of the principals is allowed the permission in the current context, else return False interface IExceptionResponse Extends: pyramid.interfaces.IException, pyramid.interfaces.IResponse An interface representing a WSGI response which is also an exception object. Register an exception view using this interface as a context to apply the registered view for all exception types raised by Pyramid internally (any exception that inherits from pyramid.response.Response, including pyramid.httpexceptions.HTTPNotFound and pyramid.httpexceptions.HTTPForbidden). prepare(environ) Prepares the response for being called as a WSGI application interface IRoute Interface representing the IRoutesMapper.get_route name The route name pattern The route pattern factory The root factory used by the Pyramid router when this route matches (or None) generate(kw) Generate a URL based on lling in the dynamic segment markers in the pattern using the kw dictionary provided. pregenerator This attribute should either be None or a callable object implementing the IRoutePregenerator interface predicates A sequence of route predicate objects used to determine if a request matches this route or not after basic pattern matching has been completed.
type
of
object
returned
from
602
48.2. OTHER INTERFACES match(path) If the path passed to this function can be matched by the pattern of this route, return a dictionary (the matchdict), which will contain keys representing the dynamic segment markers in the pattern mapped to values extracted from the provided path. If the path passed to this function cannot be matched by the pattern of this route, return None. interface IRoutePregenerator
__call__(request, elements, kw) A pregenerator is a function associated by a developer with a route. The pregenerator for a route is called by pyramid.request.Request.route_url() in order to adjust the set of arguments passed to it by the user for special purposes, such as Pylons subdomain support. It will inuence the URL returned by route_url. A pregenerator should return a two-tuple of (elements, kw) after examining the originals passed to this function, which are the arguments (request, elements, kw). The simplest pregenerator is:
You can employ a pregenerator by passing a pregenerator argument to the pyramid.config.Configurator.add_route() function. interface ISession Extends: pyramid.interfaces.IDict An interface representing a session (a web session object, usually accessed via request.session. Keys and values of a session must be pickleable. invalidate() Invalidate the session. The action caused by invalidate is implementationdependent, but it should have the effect of completely dissociating any data stored in the session with the current request. It might set response values (such as one which clears a cookie), or it might not. 603
48. PYRAMID.INTERFACES flash(msg, queue=, allow_duplicate=True) Push a ash message onto the end of the ash queue represented by queue. An alternate ash message queue can used by passing an optional queue, which must be a string. If allow_duplicate is false, if the msg already exists in the queue, it will not be readded. created Integer representing Epoch time when created. changed() Mark the session as changed. A user of a session should call this method after he or she mutates a mutable object that is a value of the session (it should not be required after mutating the session itself). For example, if the user has stored a dictionary in the session under the key foo, and he or she does session[foo] = {}, changed() neednt be called. However, if subsequently he or she does session[foo][a] = 1, changed() must be called for the sessioning machinery to notice the mutation of the internal dictionary. get_csrf_token() Return a random cross-site request forgery protection token. It will be a string. If a token was previously added to the session via new_csrf_token, that token will be returned. If no CSRF token was previously set into the session, new_csrf_token will be called, which will create and set a token, and this token will be returned. peek_flash(queue=) Peek at a queue in the ash storage. The queue remains in ash storage after this message is called. The queue is returned; it is a list of ash messages added by pyramid.interfaces.ISession.flash() new_csrf_token() Create and set into the session a new, random cross-site request forgery protection token. Return the token. It will be a string. new Boolean attribute. If True, the session is new. pop_flash(queue=) Pop a queue from the ash storage. The queue is removed from ash storage after this message is called. The queue is returned; it is a list of ash messages added by pyramid.interfaces.ISession.flash() interface ISessionFactory An interface representing a factory which accepts a request object and returns an ISession object 604
48.2. OTHER INTERFACES __call__(request) Return an ISession object interface IRendererInfo An object implementing this interface is passed to every renderer factory constructor as its only argument (conventionally named info) name The value passed by the user as the renderer name package The current package when the renderer conguration statement was found settings The deployment settings dictionary related to the current application registry The current application registry when the renderer was created type The renderer type name interface ITemplateRenderer Extends: pyramid.interfaces.IRenderer implementation() Return the object that the underlying templating system uses to render the template; it is typically a callable that accepts arbitrary keyword arguments and returns a string or unicode object interface IViewMapperFactory
__call__(self, **kw) Return an object which implements pyramid.interfaces.IViewMapper. kw will be a dictionary containing view-specic arguments, such as permission, predicates, attr, renderer, and other items. An IViewMapperFactory is used by pyramid.config.Configurator.add_view() to provide a plugpoint to extension developers who want to modify potential view callable invocation signatures and response values. interface IViewMapper
605
48. PYRAMID.INTERFACES __call__(self, object) Provided with an arbitrary object (a function, class, or instance), returns a callable with the call signature (context, request). The callable returned should itself return a Response object. An IViewMapper is returned by pyramid.interfaces.IViewMapperFactory. interface IDict
__delitem__(k) Delete an item from the dictionary which is passed to the renderer as the renderer globals dictionary. setdefault(k, default=None) Return the existing value for key k in the dictionary. If no value with k exists in the dictionary, set the default value into the dictionary under the k name passed. If a value already existed in the dictionary, return it. If a value did not exist in the dictionary, return the default __getitem__(k) Return the value for key k from the dictionary or raise a KeyError if the key doesnt exist __contains__(k) Return True if key k exists in the dictionary. keys() Return a list of keys from the dictionary items() Return a list of [(k,v)] pairs from the dictionary clear() Clear all values from the dictionary get(k, default=None) Return the value for key k from the renderer dictionary, or the default if no such value exists. __setitem__(k, value) Set a key/value pair into the dictionary
606
48.2. OTHER INTERFACES pop(k, default=None) Pop the key k from the dictionary and return its value. If k doesnt exist, and default is provided, return the default. If k doesnt exist and default is not provided, raise a KeyError. update(d) Update the renderer dictionary with another dictionary d. __iter__() Return an iterator over the keys of this dictionary has_key(k) Return True if key k exists in the dictionary. values() Return a list of values from the dictionary itervalues() Return an iterator of values from the dictionary iteritems() Return an iterator of (k,v) pairs from the dictionary popitem() Pop the item with key k from the dictionary and return it as a two-tuple (k, v). If k doesnt exist, raise a KeyError. iterkeys() Return an iterator of keys from the dictionary interface IMultiDict Extends: pyramid.interfaces.IDict An ordered dictionary that can have multiple values for each key. A multidict adds the methods getall, getone, mixed, extend add, and dict_of_lists to the normal dictionary interface. A multidict data structure is used as request.POST, request.GET, and request.params within an Pyramid application. extend(other=None, **kwargs) Add a set of keys and values, not overwriting any previous values. The other structure may be a list of two-tuples or a dictionary. If **kwargs is passed, its value will overwrite existing values.
607
48. PYRAMID.INTERFACES getall(key) Return a list of all values matching the key (may be an empty list) add(key, value) Add the key and value, not overwriting any previous value. getone(key) Get one value matching the key, raising a KeyError if multiple values were found. dict_of_lists() Returns a dictionary where each key is associated with a list of values. mixed() Returns a dictionary where the values are either single values, or a list of values when a key/value appears more than once in this dictionary. This is similar to the kind of dictionary often used to represent the variables in a web request. interface IResponse Represents a WSGI response using the WebOb response interface. Some attribute and method documentation of this interface references RFC 2616. This interface is most famously implemented by pyramid.response.Response and the HTTP exception classes in pyramid.httpexceptions. content_length Gets and sets and deletes the Content-Length header. For more information on Content-Length see RFC 2616 section 14.17. Converts using int. status The status string. encode_content(encoding=gzip, lazy=False) Encode the content with the given encoding (only gzip and identity are supported). cache_expires Get/set the Cache-Control and Expires headers. This sets the response to expire in the number of seconds passed when set. set_cookie(key, value=, max_age=None, path=/, domain=None, secure=False, httponly=False, comment=None, expires=None, overwrite=False) Set (add) a cookie for the response
608
48.2. OTHER INTERFACES vary Gets and sets and deletes the Vary header. For more information on Vary see section 14.44. Converts using list. retry_after Gets and sets and deletes the Retry-After header. For more information on RetryAfter see RFC 2616 section 14.37. Converts using HTTP date or delta seconds. www_authenticate Gets and sets and deletes the WWW-Authenticate header. For more information on WWW-Authenticate see RFC 2616 section 14.47. Converts using parse_auth and serialize_auth. content_language Gets and sets and deletes the Content-Language header. Converts using list. For more information about Content-Language see RFC 2616 section 14.12. etag Gets and sets and deletes the ETag header. For more information on ETag see RFC 2616 section 14.19. Converts using Entity tag. content_location Gets and sets and deletes the Content-Location header. For more information on Content-Location see RFC 2616 section 14.14. server Gets and sets and deletes the Server header. For more information on Server see RFC216 section 14.38. unset_cookie(key, strict=True) Unset a cookie with the given name (remove it from the response). pragma Gets and sets and deletes the Pragma header. For more information on Pragma see RFC 2616 section 14.32. app_iter Returns the app_iter of the response. If body was set, this will create an app_iter from that body (a single-item list) headers The headers in a dictionary-like object 609
48. PYRAMID.INTERFACES charset Get/set the charset (in the Content-Type) unicode_body Get/set the unicode value of the body (using the charset of the Content-Type) status_int The status as an integer conditional_response_app(environ, start_response) Like the normal __call__ interface, but checks conditional headers: If-Modied-Since (304 Not Modied; only on GET, HEAD) If-None-Match (304 Not Modied; only on GET, HEAD) Range (406 Partial Content; only on GET, HEAD) delete_cookie(key, path=/, domain=None) Delete a cookie from the client. Note that path and domain must match how the cookie was originally set. This sets the cookie to the empty string, and max_age=0 so that it should expire immediately. content_range Gets and sets and deletes the Content-Range header. For more information on Content-Range see section 14.16. Converts using ContentRange object. content_encoding Gets and sets and deletes the Content-Encoding header. For more information about Content-Encoding see RFC 2616 section 14.11. __call__(environ, start_response) WSGI call interface, should call the start_response callback and should return an iterable content_md5 Gets and sets and deletes the Content-MD5 header. For more information on Content-MD5 see RFC 2616 section 14.14. content_disposition Gets and sets and deletes the Content-Disposition header. For more information on Content-Disposition see RFC 2616 section 19.5.1. cache_control Get/set/modify the Cache-Control header (RFC 2616 section 14.9)
610
48.2. OTHER INTERFACES location Gets and sets and deletes the Location header. For more information on Location see RFC 2616 section 14.30. body The body of the response, as a str. This will read in the entire app_iter if necessary. expires Gets and sets and deletes the Expires header. For more information on Expires see RFC 2616 section 14.21. Converts using HTTP date. content_type_params A dictionary of all the parameters in the content type. This is not a view, set to change, modications of the dict would not be applied otherwise. md5_etag(body=None, set_content_md5=False) Generate an etag for the response object using an MD5 hash of the body (the body parameter, or self.body if not given). Sets self.etag. If set_content_md5 is True sets self.content_md5 as well app_iter_range(start, stop) Return a new app_iter built from the response app_iter that serves up only the given start:stop range. last_modified Gets and sets and deletes the Last-Modied header. For more information on LastModied see RFC 2616 section 14.29. Converts using HTTP date. RequestClass Alias for pyramid.request.Request content_type Get/set the Content-Type header (or None), without the charset or any parameters. If you include parameters (or ; at all) when setting the content_type, any existing parameters will be deleted; otherwise they will be preserved. date Gets and sets and deletes the Date header. For more information on Date see RFC 2616 section 14.18. Converts using HTTP date. copy() Makes a copy of the response and returns the copy.
611
48. PYRAMID.INTERFACES accept_ranges Gets and sets and deletes the Accept-Ranges header. For more information on Accept-Ranges see RFC 2616, section 14.5 age Gets and sets and deletes the Age header. Converts using int. For more information on Age see RFC 2616, section 14.6. request Return the request associated with this response if any. merge_cookies(resp) Merge the cookies that were set on this response with the given resp object (which can be any WSGI application). If the resp is a webob.Response object, then the other object will be modied in-place. headerlist The list of response headers. environ Get/set the request environ associated with this response, if any. allow Gets and sets and deletes the Allow header. Converts using list. For more information on Allow see RFC 2616, Section 14.7. body_file A le-like object that can be used to write to the body. If you passed in a list app_iter, that app_iter will be modied by writes. interface IIntrospectable An introspectable object used for conguration introspection. In addition to the methods below, objects which implement this interface must also implement all the methods of Pythons collections.MutableMapping (the dictionary interface), and must be hashable. register(introspector, action_info) Register this IIntrospectable with an introspector. This method is invoked during action execution. Adds the introspectable and its relations to the introspector. introspector should be an object implementing IIntrospector. action_info should be a object implementing the interface pyramid.interfaces.IActionInfo representing the call that registered this introspectable. Pseudocode for an implementation of this method:
612
def register(self, introspector, action_info): self.action_info = action_info introspector.add(self) for methodname, category_name, discriminator in self._relations: method = getattr(introspector, methodname) method((i.category_name, i.discriminator), (category_name, discriminator))
discriminator_hash an integer hash of the discriminator title Text title describing this introspectable relate(category_name, discriminator) Indicate an intent to relate this IIntrospectable with another IIntrospectable (the one associated with the category_name and discriminator) during action execution. type_name Text type name describing this introspectable unrelate(category_name, discriminator) Indicate an intent to break the relationship between this IIntrospectable with another IIntrospectable (the one associated with the category_name and discriminator) during action execution. action_info An IActionInfo object representing the caller that invoked the creation of this introspectable (usually a sentinel until updated during self.register) __hash__() Introspectables must be hashable. trosepectables __hash__ is:
48. PYRAMID.INTERFACES order integer order in which registered with introspector (managed by introspector, usually) category_name introspection category name interface IIntrospector
get(category_name, discriminator, default=None) Get the IIntrospectable related to the category_name and the discriminator (or discriminator hash) discriminator. If it does not exist in the introspector, return the value of default relate(*pairs) Given any number of (category_name, discriminator) pairs passed as positional arguments, relate the associated introspectables to each other. The introspectable related to each pair must have already been added via .add or .add_intr; a KeyError will result if this is not true. An error will not be raised if any pair has already been associated with another. This method is not typically called directly, instead its called indirectly by pyramid.interfaces.IIntrospector.register() get_category(category_name, default=None, sort_key=None) Get a sequence of dictionaries in the form [{introspectable:IIntrospectable, related:[sequence of related IIntrospectables]}, ...] where each introspectable is part of the category associated with category_name . If the category named category_name does not exist in the introspector the value passed as default will be returned. If sort_key is None, the sequence will be returned in the order the introspectables were added to the introspector. Otherwise, sort_key should be a function that accepts an IIntrospectable and returns a value from it (ala the key function of Pythons sorted callable).
614
48.2. OTHER INTERFACES unrelate(*pairs) Given any number of (category_name, discriminator) pairs passed as positional arguments, unrelate the associated introspectables from each other. The introspectable related to each pair must have already been added via .add or .add_intr; a KeyError will result if this is not true. An error will not be raised if any pair is not already related to another. This method is not typically called directly, instead its called indirectly by pyramid.interfaces.IIntrospector.register() remove(category_name, discriminator) Remove the IIntrospectable related to category_name and discriminator from the introspector, and x up any relations that the introspectable participates in. This method will not raise an error if an introspectable related to the category name and discriminator does not exist. add(intr) Add the IIntrospectable intr (use instead of pyramid.interfaces.IIntrospector.add() when you have a custom IIntrospectable). Replaces any existing introspectable registered using the same category/discriminator. This method is not typically called directly, instead its called indirectly by pyramid.interfaces.IIntrospector.register() related(intr) Return a sequence of IIntrospectables related to the IIntrospectable intr. Return the empty sequence if no relations for exist. categorized(sort_key=None) Get a sequence of tuples in the form [(category_name, [{introspectable:IIntrospectable, related:[sequence of related IIntrospectables]}, ...])] representing all known introspectables. If sort_key is None, each introspectables sequence will be returned in the order the introspectables were added to the introspector. Otherwise, sort_key should be a function that accepts an IIntrospectable and returns a value from it (ala the key function of Pythons sorted callable). categories() Return a sorted sequence of category names known by this introspector interface IActionInfo Class which provides code introspection capability associated with an action. The ParserInfo class used by ZCML implements the same interface. 615
48. PYRAMID.INTERFACES line Starting line number in le (as an integer) of action-invoking code.This will be None if the value could not be determined. file Filename of action-invoking code as a string __str__() Return a representation of the action information (including source code from le, if possible) interface IAssetDescriptor Describes an asset. isdir() Returns True if the asset is a directory, otherwise returns False. exists() Returns True if asset exists, otherwise returns False. stream() Returns an input stream for reading asset contents. Raises an exception if the asset is a directory or does not exist. abspath() Returns an absolute path in the lesystem to the asset. listdir() Returns iterable of lenames of directory contents. Raises an exception if asset is not a directory. absspec() Returns the absolute asset specication mypackage:templates/foo.pt). interface IResourceURL
for
this
asset
(e.g.
virtual_path The virtual url path of the resource. physical_path The physical url path of the resource. 616
CHAPTER
FORTYNINE
PYRAMID.LOCATION
lineage(resource) Return a generator representing the lineage of the resource object implied by the resource argument. The generator rst returns resource unconditionally. Then, if resource supplies a __parent__ attribute, return the resource represented by resource.__parent__. If that resource has a __parent__ attribute, return that resources parent, and so on, until the resource being inspected either has no __parent__ attribute or which has a __parent__ attribute of None. For example, if the resource tree is:
thing1 = Thing() thing2 = Thing() thing2.__parent__ = thing1
Calling lineage(thing2) will return a generator. When we turn it into a list, we will get:
list(lineage(thing2)) [ <Thing object at thing2>, <Thing object at thing1> ]
inside(resource1, resource2) Is resource1 inside resource2? Return True if so, else False. resource1 is inside resource2 if resource2 is a lineage ancestor of resource1. It is a lineage ancestor if its parent (or one of its parents parents, etc.) is an ancestor.
617
49. PYRAMID.LOCATION
618
CHAPTER
FIFTY
PYRAMID.PASTER
bootstrap(cong_uri, request=None, options=None) Load a WSGI application from the PasteDeploy cong le specied by config_uri. The environment will be congured as if it is currently serving request, leaving a natural environment in place to write scripts that can generate URLs and utilize renderers. This function returns a dictionary with app, root, closer, request, and registry keys. app is the WSGI app loaded (based on the config_uri), root is the traversal root resource of the Pyramid application, and closer is a parameterless callback that may be called when your script is complete (it pops a threadlocal stack).
Most operations within Pyramid expect to be invoked within the context of a WSGI request, thus its important when loading your application to anchor it when executing scripts and other code that is not normally invoked during active WSGI requests.
For a complex cong le containing multiple Pyramid applications, this function will setup the environment under the context of the last-loaded Pyramid application. You may load a specic application yourself by using the lower-level functions pyramid.paster.get_app() and pyramid.scripting.prepare() in conjunction with pyramid.config.global_registries.
config_uri species the PasteDeploy cong le to use for the interactive shell. The format is inifile#name. If the name is left off, main will be assumed.
619
50. PYRAMID.PASTER request specied to anchor the script to a given set of WSGI parameters. For example, most people would want to specify the host, scheme and port such that their script will generate URLs in relation to those parameters. A request with default parameters is constructed for you if none is provided. You can mutate the requests environ later to setup a specic host/port/scheme/etc. options Is passed to get_app for use as variable assignments like {http_port: 8080} and then use %(http_port)s in the cong le. See Writing a Script for more information about how to use this function. get_app(cong_uri, name=None, options=None) Return the WSGI application named name in the PasteDeploy cong le specied by config_uri. options, if passed, should be a dictionary used as variable assignments like {http_port: 8080}. This is useful if e.g. %(http_port)s is used in the cong le. If the name is None, this will attempt to parse the name from the config_uri string expecting the format inifile#name. If no name is found, the name will default to main. get_appsettings(cong_uri, name=None) Return a dictionary representing the key/value pairs in an app section within the le represented by config_uri. If the name is None, this will attempt to parse the name from the config_uri string expecting the format inifile#name. If no name is found, the name will default to main. setup_logging(cong_uri) Set up logging via the logging modules leCong function with the lename specied via config_uri (a string in the form filename#sectionname). CongParser defaults are specied for the special __file__ and here variables, similar to PasteDeploy cong loading.
620
CHAPTER
FIFTYONE
PYRAMID.REGISTRY
class Registry(name=, bases=()) A registry object is an application registry. It is used by the framework itself to perform mappings of URLs to view callables, as well as servicing other various framework duties. A registry has its own internal API, but this API is rarely used by Pyramid application developers (its usually only used by developers of the Pyramid framework). But it has a number of attributes that may be useful to application developers within application code, such as settings, which is a dictionary containing application deployment settings. For information about the purpose and usage of the application registry, see Using the Zope Component Architecture in Pyramid. The application registry is usually accessed as request.registry in application code. settings The dictionary-like deployment settings object. See Deployment Settings for information. This object is often accessed as request.registry.settings or config.registry.settings in a typical Pyramid application. introspector When a registry is set up (or created) by a Congurator, the registry will be decorated with an instance named introspector implementing the pyramid.interfaces.IIntrospector interface. See also pyramid.config.Configurator.introspector. When a registry is created by hand, however, this attribute will not exist until set up by a congurator. This attribute is often accessed as request.registry.introspector in a typical Pyramid application. This attribute is new as of Pyramid 1.3. 621
51. PYRAMID.REGISTRY class Introspectable The default implementation of the interface pyramid.interfaces.IIntrospectable used by framework exenders. An instance of this class is created when pyramid.config.Configurator.introspectable is called. This class is new as of Pyramid 1.3. class Deferred(func) Can be used by a third-party conguration extender to wrap a discriminator during conguration if an immediately hashable discriminator cannot be computed because it relies on unresolved values. The function should accept no arguments and should return a hashable discriminator. This class is new as of Pyramid 1.4. undefer(v) Function which accepts an object and returns it unless it is a pyramid.registry.Deferred instance. If it is an instance of that class, its resolve method is called, and the result of the method is returned. This function is new as of Pyramid 1.4. class predvalseq A subtype of tuple used to represent a sequence of predicate values This class is new as of Pyramid 1.4.
622
CHAPTER
FIFTYTWO
PYRAMID.RENDERERS
get_renderer(renderer_name, package=None) Return the renderer object for the renderer named as renderer_name. You may supply a relative asset spec as renderer_name. If the package argument is supplied, a relative renderer name will be converted to an absolute asset specication by combining the package supplied as package with the relative asset specication supplied as renderer_name. If you do not supply a package (or package is None) the package name of the caller of this function will be used as the package. render(renderer_name, value, request=None, package=None) Using the renderer specied as renderer_name (a template or a static renderer) render the value (or set of values) present in value. Return the result of the renderers __call__ method (usually a string or Unicode). If the renderer name refers to a le on disk (such as when the renderer is a template), its usually best to supply the name as a asset specication (e.g. packagename:path/to/template.pt). You may supply a relative asset spec as renderer_name. If the package argument is supplied, a relative renderer path will be converted to an absolute asset specication by combining the package supplied as package with the relative asset specication supplied as renderer_name. If you do not supply a package (or package is None) the package name of the caller of this function will be used as the package. The value provided will be supplied as the input to the renderer. Usually, for template renderings, this should be a dictionary. For other renderers, this will need to be whatever sort of value the renderer expects. 623
52. PYRAMID.RENDERERS The system values supplied to the renderer will include a basic set of top-level system names, such as request, context, renderer_name, and view. See System Values Used During Rendering for the full list. If renderer globals have been specied, these will also be used to agument the value. Supply a request parameter in order to provide the renderer with the most correct system values (request and context in particular). render_to_response(renderer_name, value, request=None, package=None) Using the renderer specied as renderer_name (a template or a static renderer) render the value (or set of values) using the result of the renderers __call__ method (usually a string or Unicode) as the response body. If the renderer name refers to a le on disk (such as when the renderer is a template), its usually best to supply the name as a asset specication. You may supply a relative asset spec as renderer_name. If the package argument is supplied, a relative renderer name will be converted to an absolute asset specication by combining the package supplied as package with the relative asset specication supplied as renderer_name. If you do not supply a package (or package is None) the package name of the caller of this function will be used as the package. The value provided will be supplied as the input to the renderer. Usually, for template renderings, this should be a dictionary. For other renderers, this will need to be whatever sort of value the renderer expects. The system values supplied to the renderer will include a basic set of top-level system names, such as request, context, renderer_name, and view. See System Values Used During Rendering for the full list. If renderer globals have been specied, these will also be used to agument the value. Supply a request parameter in order to provide the renderer with the most correct system values (request and context in particular). Keep in mind that if the request parameter is not passed in, any changes to request.response attributes made before calling this function will be ignored. class JSON(serializer=<function dumps at 0x40c6500>, adapters=(), **kw) Renderer that returns a JSON-encoded string. Congure a custom JSON renderer using the add_renderer() API at application startup time:
from pyramid.config import Configurator config = Configurator() config.add_renderer(myjson, JSON(indent=4))
624
Once this renderer is registered as above, you can use myjson as the renderer= parameter to @view_config or add_view():
from pyramid.view import view_config @view_config(renderer=myjson) def myview(request): return {greeting:Hello world}
Custom objects can be serialized using the renderer by either implementing the __json__ magic method, or by registering adapters with the renderer. See Serializing Custom Objects for more information. The default serializer uses json.JSONEncoder. A different serializer can be specied via the serializer argument. Custom serializers should accept the object, a callback default, and any extra kw keyword argments passed during renderer construction.
This feature is new in Pyramid 1.4. Prior to 1.4 there was no public API for supplying options to the underlying serializer without dening a custom renderer.
class JSONP(param_name=callback, **kw) JSONP renderer factory helper which implements a hybrid json/jsonp renderer. JSONP is useful for making cross-domain AJAX requests. Congure a JSONP renderer using the pyramid.config.Configurator.add_renderer() API at application startup time:
from pyramid.config import Configurator config = Configurator() config.add_renderer(jsonp, JSONP(param_name=callback))
The class constructor also accepts arbitrary keyword arguments. All keyword arguments except param_name are passed to the json.dumps function as its keyword arguments.
from pyramid.config import Configurator config = Configurator() config.add_renderer(jsonp, JSONP(param_name=callback, indent=4))
625
52. PYRAMID.RENDERERS
The ability of this class to accept a **kw in its constructor is new as of Pyramid 1.4.
The arguments passed to this class constructor mean the same thing as the arguments passed to pyramid.renderers.JSON (including serializer and adapters). Once this renderer is registered via add_renderer() as above, you can use jsonp as the renderer= parameter to @view_config or pyramid.config.Configurator.add_view():
from pyramid.view import view_config @view_config(renderer=jsonp) def myview(request): return {greeting:Hello world}
When a view is called that uses the JSONP renderer: If there is a parameter in the requests HTTP query string that matches the param_name of the registered JSONP renderer (by default, callback), the renderer will return a JSONP response. If there is no callback parameter in the requests query string, the renderer will return a plain JSON response.
See also: JSONP Renderer. null_renderer An object that can be used in advanced integration cases as input to the view conguration renderer= argument. When the null renderer is used as a view renderer argument, Pyramid avoids converting the view callable result into a Response object. This is useful if you want to reuse the view conguration and lookup machinery outside the context of its use by the Pyramid router.
626
CHAPTER
FIFTYTHREE
PYRAMID.REQUEST
class Request(environ, charset=None, unicode_errors=None, decode_param_names=None, **kw) A subclass of the WebOb Request class. An instance of this class is created by the router and is provided to a view callable (and to other subsystems) as the request argument. The documentation below (save for the add_response_callback and add_finished_callback methods, which are dened in this subclass itself, and the attributes context, registry, root, subpath, traversed, view_name, virtual_root , and virtual_root_path, each of which is added to the request by the router at request ingress time) are autogenerated from the WebOb source code used when this documentation was generated. Due to technical constraints, we cant yet display the WebOb version number from which this documentation is autogenerated, but it will be the prevailing WebOb version at the time of the release of this Pyramid version. See http://pythonpaste.org/webob/ for further information. context The context will be available as the context attribute of the request object. It will be the context object implied by the current request. See Traversal for information about context objects. registry The application registry will be available as the registry attribute of the request object. See Using the Zope Component Architecture in Pyramid for more information about the application registry. root The root object will be available as the root attribute of the request object. It will be the resource object at which traversal started (the root). See Traversal for information about root objects. 627
53. PYRAMID.REQUEST subpath The traversal subpath will be available as the subpath attribute of the request object. It will be a sequence containing zero or more elements (which will be Unicode objects). See Traversal for information about the subpath. traversed The traversal path will be available as the traversed attribute of the request object. It will be a sequence representing the ordered set of names that were used to traverse to the context, not including the view name or subpath. If there is a virtual root associated with the request, the virtual root path is included within the traversal path. See Traversal for more information. view_name The view name will be available as the view_name attribute of the request object. It will be a single string (possibly the empty string if were rendering a default view). See Traversal for information about view names. virtual_root The virtual root will be available as the virtual_root attribute of the request object. It will be the virtual root object implied by the current request. See Virtual Hosting for more information about virtual roots. virtual_root_path The virtual root path will be available as the virtual_root_path attribute of the request object. It will be a sequence representing the ordered set of names that were used to traverse to the virtual root object. See Virtual Hosting for more information about virtual roots. exception If an exception was raised by a root factory or a view callable, or at various other points where Pyramid executes user-dened code during the processing of a request, the exception object which was caught will be available as the exception attribute of the request within a exception view, a response callback or a nished callback. If no exception occurred, the value of request.exception will be None within response and nished callbacks. exc_info If an exception was raised by a root factory or a view callable, or at various other points where Pyramid executes user-dened code during the processing of a request, result of sys.exc_info() will be available as the exc_info attribute of the request within a exception view, a response callback or a nished callback. If no exception occurred, the value of request.exc_info will be None within response and nished callbacks.
628
response This attribute is actually a reied property which returns an instance of the pyramid.response.Response class. The response object returned does not exist until this attribute is accessed. Once it is accessed, subsequent accesses to this request object will return the same Response object. The request.response API can is used by renderers. A render obtains the response object it will return from a view that uses that renderer by accessing request.response. Therefore, its possible to use the request.response API to set up a response object with the right attributes (e.g. by calling request.response.set_cookie(...) or request.response.content_type = text/plain, etc) within a view that uses a renderer. For example, within a view that uses a renderer:
response = request.response response.set_cookie(mycookie, mine, all mine!) return {text:Value that will be used by the renderer}
Mutations to this response object will be preserved in the response sent to the client after rendering. For more information about using request.response in conjunction with a renderer, see Varying Attributes of Rendered Responses. Non-renderer code can also make use of request.response instead of creating a response by hand. For example, in view code:
response = request.response response.body = Hello! response.content_type = text/plain return response
Note that the response in this circumstance is not global; it still must be returned from the view code if a renderer is not used. session If a session factory has been congured, this attribute will represent the current users session object. If a session factory has not been congured, requesting the request.session attribute will cause a pyramid.exceptions.ConfigurationError to be raised. matchdict If a route has matched during this request, this attribute will be a dictionary containing the values matched by the URL pattern associated with the route. If a route has not matched during this request, the value of this attribute will be None. See The Matchdict.
629
53. PYRAMID.REQUEST matched_route If a route has matched during this request, this attribute will be an obect representing the route matched by the URL pattern associated with the route. If a route has not matched during this request, the value of this attribute will be None. See The Matched Route. invoke_subrequest(request, use_tweens=False) This API was added in Pyramid 1.4a1.
Obtain a response object from the Pyramid application based on information in the request object provided. The request object must be an object that implements the Pyramid request interface (such as a pyramid.request.Request instance). If use_tweens is True, the request will be sent to the tween in the tween stack closest to the request ingress. If use_tweens is False, the request will be sent to the main router handler, and no tweens will be invoked. This function also: manages the threadlocal stack (so that get_current_request() get_current_registry() work during a request) Adds a registry attribute (the current Pyramid registry) and invoke_subrequest attribute (a callable) to the request object its handed. and
sets request extensions (such as those added via add_request_method() or set_request_property()) on the request its passed. causes a NewRequest event to be sent at the beginning of request processing. causes a ContextFound event to be sent when a context resource is found. Ensures that the user implied by the request passed has the necessary authorization to invoke view callable before calling it. causes a NewResponse event to be sent when the Pyramid application returns a response. Calls any response callback functions dened within the requests lifetime if a response is obtained from the Pyramid application. Calls any nished callback functions dened within the requests lifetime.
630
invoke_subrequest isnt actually a method of the Request object; its a callable added when the Pyramid router is invoked, or when a subrequest is invoked. This means that its not available for use on a request provided by e.g. the pshell environment. For more information, see Invoking a Subrequest. add_response_callback(callback) Add a callback to the set of callbacks to be called by the router at a point after a response object is successfully created. Pyramid does not have a global response object: this functionality allows an application to register an action to be performed against the response once one is created. A callback is a callable which accepts two positional parameters: response. For example:
1 2 3 4
request and
def cache_callback(request, response): Set the cache_control max_age for the response response.cache_control.max_age = 360 request.add_response_callback(cache_callback)
Response callbacks are called in the order theyre added (rst-to-most-recently-added). No response callback is called if an exception happens in application code, or if the response object returned by view code is invalid. All response callbacks are called after the pyramid.events.NewResponse event is sent. Errors raised by callbacks are not handled specially. They will be propagated to the caller of the Pyramid router application. See also: Using Response Callbacks. add_finished_callback(callback) Add a callback to the set of callbacks to be called unconditionally by the router at the very end of request processing. callback is a callable which accepts a single positional parameter: request. For example:
1 2 3 4 5 6 7 8 9
import transaction def commit_callback(request): commit or abort the transaction associated with request if request.exception is not None: transaction.abort() else: transaction.commit() request.add_finished_callback(commit_callback)
631
53. PYRAMID.REQUEST Finished callbacks are called in the order theyre added ( rst- to most-recently- added). Finished callbacks (unlike response callbacks) are always called, even if an exception happens in application code that prevents a response from being generated. The set of nished callbacks associated with a request are called very late in the processing of that request; they are essentially the last thing called by the router. They are called after response processing has already occurred in a top-level finally: block within the router request processing code. As a result, mutations performed to the request provided to a nished callback will have no meaningful effect, because response processing will have already occurred, and the requests scope will expire almost immediately after all nished callbacks have been processed. Errors raised by nished callbacks are not handled specially. They will be propagated to the caller of the Pyramid router application. See also: Using Finished Callbacks. route_url(route_name, *elements, **kw) Generates a fully qualied URL for a named Pyramid route conguration. Use the routes name as the rst positional argument. Additional positional arguments (*elements) are appended to the URL as path segments after it is generated. Use keyword arguments to supply values which match any dynamic path elements in the route denition. Raises a KeyError exception if the URL cannot be generated for any reason (not enough arguments, for example). For example, if youve dened {foo}/{bar}/*traverse: a route named foobar with the path
request.route_url(foobar, foo=1) request.route_url(foobar, foo=1, bar=2) request.route_url(foobar, foo=1, bar=2, traverse=(a,b)) request.route_url(foobar, foo=1, bar=2, traverse=/a/b)
=> http://e.com/1/2/a/b
=> http://e.com/1/2/a/b
632
Values replacing :segment arguments can be passed as strings or Unicode objects. They will be encoded to UTF-8 and URL-quoted before being placed into the generated URL. Values replacing *remainder arguments can be passed as strings or tuples of Unicode/string values. If a tuple is passed as a *remainder replacement value, its values are URL-quoted and encoded to UTF-8. The resulting strings are joined with slashes and rendered into the URL. If a string is passed as a *remainder replacement value, it is tacked on to the URL after being URL-quoted-except-for-embedded-slashes. If a keyword argument _query is present, it will be used to compose a query string that will be tacked on to the end of the URL. The value of _query must be a sequence of two-tuples or a data structure with an .items() method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a query string per the documentation of pyramid.encode.urlencode() function. After the query data is turned into a query string, a leading ? is prepended, and the resulting string is appended to the generated URL.
Python data structures that are passed as _query which are sequences or dictionaries are turned into a string under the same rules as when run through urllib.urlencode() with the doseq argument equal to True. This means that sequences can be passed as values, and a k=v pair will be placed into the query string for each value. If a keyword argument _anchor is present, its string representation will be used as a named anchor in the generated URL (e.g. if _anchor is passed as foo and the route URL is http://example.com/route/url, the resulting generated URL will be http://example.com/route/url#foo).
If _anchor is passed as a string, it should be UTF-8 encoded. If _anchor is passed as a Unicode object, it will be converted to UTF-8 before being appended to the URL. The anchor value is not quoted in any way before being appended to the generated URL. If both _anchor and _query are specied, the anchor element will always follow the query element, e.g. http://example.com?foo=1#bar. If any of the keyword arguments _scheme, _host, or _port is passed and is non-None, the provided value will replace the named portion in the generated URL. For example, if you pass _host=foo.com, and the URL that would have been generated without the host replacement is http://example.com/a, the result will be https://foo.com/a. Note that if _scheme is passed as https, and _port is not passed, the _port value is assumed to have been passed as 443. Likewise, if _scheme is passed as http and _port 633
53. PYRAMID.REQUEST is not passed, the _port value is assumed to have been passed as 80. To avoid this behavior, always explicitly pass _port whenever you pass _scheme. If a keyword _app_url is present, it will be used as the protocol/hostname/port/leading path prex of the generated URL. For example, using an _app_url of http://example.com:8080/foo would cause the URL http://example.com:8080/foo/fleeb/flub to be returned from this function if the expansion of the route pattern associated with the route_name expanded to /fleeb/flub. If _app_url is not specied, the result of request.application_url will be used as the prex (the default). If both _app_url and any of _scheme, _host, or _port are passed, _app_url takes precedence and any values passed for _scheme, _host, and _port will be ignored. This function raises a KeyError if the URL cannot be generated due to missing replacement names. Extra replacement names are ignored. If the route object which matches the route_name argument has a pregenerator, the *elements and **kw arguments passed to this function might be augmented or changed. route_path(route_name, *elements, **kw) Generates a path (aka a relative URL, a URL minus the host, scheme, and port) for a named Pyramid route conguration. This function accepts the same argument as pyramid.request.Request.route_url() and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string. For example, if youve dened a route named foobar with the path /{foo}/{bar}, this call to route_path:
request.route_path(foobar, foo=1, bar=2)
Calling request.route_path(route) is the same as calling request.route_url(route, _app_url=request.script_name). pyramid.request.Request.route_path() is, in fact, implemented in terms of pyramid.request.Request.route_url() in just this way. As a result, any _app_url passed within the **kw values to route_path will be ignored.
634
current_route_url(*elements, **kw) Generates a fully qualied URL for a named Pyramid route conguration based on the current route. This function supplements pyramid.request.Request.route_url(). It presents an easy way to generate a URL for the current route (dened as the route which matched when the request was generated). The arguments to this method have the same meaning as those with the same names passed to pyramid.request.Request.route_url(). It also understands an extra argument which route_url does not named _route_name. The route name used to generate a URL is taken from either the _route_name keyword argument or the name of the route which is currently associated with the request if _route_name was not passed. Keys and values from the current request matchdict are combined with the kw arguments to form a set of defaults named newkw. Then request.route_url(route_name, *elements, **newkw) is called, returning a URL. Examples follow. If the current route has the route pattern /foo/{page} and the current url path is /foo/1 , the matchdict will be {page:1}. The result of request.current_route_url() in this situation will be /foo/1. If the current route has the route pattern /foo/{page} and the current url path is /foo/1, the matchdict will be {page:1}. The result of request.current_route_url(page=2) in this situation will be /foo/2. Usage of the _route_name keyword argument: if our routing table denes routes /foo/{action} named foo and /foo/{action}/{page} named fooaction, and the current url pattern is /foo/view (which has matched the /foo/{action} route), we may want to use the matchdict args to generate a URL to the fooaction route. In this scenario, request.current_route_url(_route_name=fooaction, page=5) Will return string like: /foo/view/5. current_route_path(*elements, **kw) Generates a path (aka a relative URL, a URL minus the host, scheme, and port) for the Pyramid route conguration matched by the current request.
This function accepts the same argument as pyramid.request.Request.current_route_url() and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string. For example, if the route matched by the current request has the pattern /{foo}/{bar}, this call to current_route_path: 635
53. PYRAMID.REQUEST
request.current_route_path(foo=1, bar=2)
Calling request.current_route_path(route) is the same as calling request.current_route_url(route, _app_url=request.script_name). pyramid.request.Request.current_route_path( is, in fact, implemented in terms of pyramid.request.Request.current_route_url() in just this way. As a result, any _app_url passed within the **kw values to current_route_path will be ignored.
static_url(path, **kw) Generates a fully qualied URL for a static asset. The asset must live within a location dened via the pyramid.config.Configurator.add_static_view() conguration declaration (see Serving Static Assets). Example:
request.static_url(mypackage:static/foo.css) => http://example.com/static/foo.css
The path argument points at a le or directory on disk which a URL should be generated for. The path may be either a relative path (e.g. static/foo.css) or an absolute path (e.g. /abspath/to/static/foo.css) or a asset specication (e.g. mypackage:static/foo.css). The purpose of the **kw argument is the same as the purpose of the pyramid.request.Request.route_url() **kw argument. See the documentation for that function to understand the arguments which you can provide to it. However, typically, you dont need to pass anything as *kw when generating a static asset URL. This function raises a ValueError if a static view denition cannot be found which matches the path specication.
636
static_path(path, **kw) Generates a path (aka a relative URL, a URL minus the host, scheme, and port) for a static resource. This function accepts the same argument as pyramid.request.Request.static_url() and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string. Example:
request.static_path(mypackage:static/foo.css) => /static/foo.css
Calling request.static_path(apath) is the same as calling request.static_url(apath, _app_url=request.script_name). pyramid.request.Request.static_path() is, in fact, implemented in terms of :meth:pyramid.request.Request.static_url in just this way. As a result, any _app_url passed within the **kw values to static_path will be ignored. resource_url(resource, *elements, **kw) Generate a string representing the absolute URL of the resource object based on the wsgi.url_scheme, HTTP_HOST or SERVER_NAME in the request, plus any SCRIPT_NAME. The overall result of this method is always a UTF-8 encoded string. Examples:
request.resource_url(resource) => http://example.com/ request.resource_url(resource, a.html) => http://example.com/a.html request.resource_url(resource, a.html, query={q:1}) => http://example.com/a.html?q=1 request.resource_url(resource, a.html, anchor=abc) =>
637
53. PYRAMID.REQUEST
Any positional arguments passed in as elements must be strings Unicode objects, or integer objects. These will be joined by slashes and appended to the generated resource URL. Each of the elements passed in is URL-quoted before being appended; if any element is Unicode, it will converted to a UTF-8 bytestring before being URL-quoted. If any element is an integer, it will be converted to its string representation before being URL-quoted.
if no elements arguments are specied, the resource URL will end with a trailing slash. If any elements are used, the generated URL will not end in trailing a slash.
If a keyword argument query is present, it will be used to compose a query string that will be tacked on to the end of the URL. The value of query must be a sequence of two-tuples or a data structure with an .items() method that returns a sequence of two-tuples (presumably a dictionary). This data structure will be turned into a query string per the documentation of pyramid.url.urlencode function. After the query data is turned into a query string, a leading ? is prepended, and the resulting string is appended to the generated URL.
Python data structures that are passed as query which are sequences or dictionaries are turned into a string under the same rules as when run through urllib.urlencode() with the doseq argument equal to True. This means that sequences can be passed as values, and a k=v pair will be placed into the query string for each value. If a keyword argument anchor is present, its string representation will be used as a named anchor in the generated URL (e.g. if anchor is passed as foo and the resource URL is http://example.com/resource/url, the resulting generated URL will be http://example.com/resource/url#foo).
If anchor is passed as a string, it should be UTF-8 encoded. If anchor is passed as a Unicode object, it will be converted to UTF-8 before being appended to the URL. The anchor value is not quoted in any way before being appended to the generated URL.
638
If both anchor and query are specied, the anchor element will always follow the query element, e.g. http://example.com?foo=1#bar. If any of the keyword arguments scheme, host, or port is passed and is non-None, the provided value will replace the named portion in the generated URL. For example, if you pass host=foo.com, and the URL that would have been generated without the host replacement is http://example.com/a, the result will be https://foo.com/a. If scheme is passed as https, and an explicit port is not passed, the port value is assumed to have been passed as 443. Likewise, if scheme is passed as http and port is not passed, the port value is assumed to have been passed as 80. To avoid this behavior, always explicitly pass port whenever you pass scheme. If a keyword argument app_url is passed and is not None, it should be a string that will be used as the port/hostname/initial path portion of the generated URL instead of the default request application URL. For example, if app_url=http://foo, then the resulting url of a resource that has a path of /baz/bar will be http://foo/baz/bar. If you want to generate completely relative URLs with no leading scheme, host, port, or initial path, you can pass app_url=. Passing app_url= when the resource path is /baz/bar will return /baz/bar. app_url is new as of Pyramid 1.3. If app_url is passed and any of scheme, port, or host are also passed, app_url will take precedence and the values passed for scheme, host, and/or port will be ignored. If the resource passed in has a __resource_url__ method, it will be used to generate the URL (scheme, host, port, path) that for the base resource which is operated upon by this function. See also Overriding Resource URL Generation. If the resource used is the result of a traversal, it must be location-aware. The resource can also be the context of a URL dispatch; contexts found this way do not need to be location-aware.
If a virtual root path is present in the request environment (the value of the WSGI environ key HTTP_X_VHM_ROOT), and the resource was obtained via traversal, the URL path will not include the virtual root prex (it will be stripped off the left hand side of the generated URL).
For backwards compatibility purposes, this method is also aliased as the model_url method of request.
639
53. PYRAMID.REQUEST resource_path(resource, *elements, **kw) Generates a path (aka a relative URL, a URL minus the host, scheme, and port) for a resource. This function accepts the same argument as pyramid.request.Request.resource_url() and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string.
Calling request.resource_path(resource) is the same as calling request.resource_path(resource, app_url=request.script_name). pyramid.request.Request.resource_path() is, in fact, implemented in terms of pyramid.request.Request.resource_url() in just this way. As a result, any app_url passed within the **kw values to route_path will be ignored. scheme, host, and port are also ignored.
response_* In Pyramid 1.0, you could set attributes on a pyramid.request.Request which inuenced the behavor of rendered responses (views which use a renderer and which dont directly return a response). These attributes began with response_, such as response_headerlist. If you needed to inuence response values from a view that uses a renderer (such as the status code, a header, the content type, etc) you would set these attributes. See Deprecated Mechanism to Vary Attributes of Rendered Responses for further discussion. As of Pyramid 1.1, assignment to response_* attrs are deprecated. Assigning to one is still supported but will cause a deprecation warning to be emitted, and eventually the feature will be removed. For new code, instead of assigning response_* attributes to the request, use API of the pyramid.request.Request.response object (exposed to view code as request.response) to inuence rendered response behavior. json_body This property will return the JSON-decoded variant of the request body. If the request body is not well-formed JSON, or there is no body associated with this request, this property will raise an exception. See also Dealing With A JSON-Encoded Request Body. set_property(callable, name=None, reify=False) Add a callable or a property descriptor to the request instance. Properties, unlike attributes, are lazily evaluated by executing an underlying callable when accessed. They can be useful for adding features to an object without any cost if those features go unused.
640
A property may also be reied via the pyramid.decorator.reify decorator by setting reify=True, allowing the result of the evaluation to be cached. Thus the value of the property is only computed once for the lifetime of the object. callable can either be a callable that accepts the request as its single positional parameter, or it can be a property descriptor. If the callable is a property descriptor a ValueError will be raised if name is None or reify is True. If name is None, the name of the property will be computed from the name of the callable.
1 2 3 4 5 6 7 8 9 10 11
def _connect(request): conn = request.registry.dbsession() def cleanup(_): conn.close() request.add_finished_callback(cleanup) return conn @subscriber(NewRequest) def new_request(event): request = event.request request.set_property(_connect, db, reify=True)
The subscriber doesnt actually connect to the database, it just provides the API which, when accessed via request.db, will create the connection. Thanks to reify, only one connection is made per-request even if request.db is accessed many times. This pattern provides a way to augment the request object without having to subclass it, which can be useful for extension authors. New in version 1.3. GET Return a MultiDict containing all the variables from the QUERY_STRING. POST Return a MultiDict containing all the variables from a form request. Returns an empty dictlike object for non-form requests. Form requests are typically POST requests, however PUT requests with an appropriate Content-Type are also supported. accept Gets and sets the Accept header (HTTP spec section 14.1). 641
53. PYRAMID.REQUEST accept_charset Gets and sets the Accept-Charset header (HTTP spec section 14.2). accept_encoding Gets and sets the Accept-Encoding header (HTTP spec section 14.3). accept_language Gets and sets the Accept-Language header (HTTP spec section 14.4). application_url The URL including SCRIPT_NAME (no PATH_INFO or query string) as_bytes(skip_body=False) Return HTTP bytes representing this request. If skip_body is True, exclude the body. If skip_body is an integer larger than one, skip body only if its length is bigger than that number. authorization Gets and sets the Authorization header (HTTP spec section 14.8). Converts it using parse_auth and serialize_auth. classmethod blank(path, environ=None, base_url=None, headers=None, POST=None, **kw) Create a blank request environ (and Request wrapper) with the given path (path should be urlencoded), and any keys from environ. The path will become path_info, with any query string split off and used. All necessary keys will be added to the environ, but the values you pass in will take precedence. If you pass in base_url then wsgi.url_scheme, HTTP_HOST, and SCRIPT_NAME will be lled in from that value. Any extra keyword will be passed to __init__. body Return the content of the request body. body_file Input stream of the request (wsgi.input). Setting this property resets the content_length and seekable ag (unlike setting req.body_le_raw). body_file_raw Gets and sets the wsgi.input key in the environment.
642
body_file_seekable Get the body of the request (wsgi.input) as a seekable le-like object. Middleware and routing applications should use this attribute over .body_le. If you access this value, CONTENT_LENGTH will also be updated. cache_control Get/set/modify the Cache-Control header (HTTP spec section 14.9) call_application(application, catch_exc_info=False) Call the given WSGI application, returning (status_string, headerlist, app_iter) Be sure to call app_iter.close() if its there. If catch_exc_info is true, then returns (status_string, headerlist, app_iter, exc_info), where the fourth item may be None, but wont be if there was an exception. If you dont do this and there was an exception, the exception will be raised directly. client_addr The effective client IP address as a string. If the HTTP_X_FORWARDED_FOR header exists in the WSGI environ, this attribute returns the client IP address present in that header (e.g. if the header value is 192.168.1.1, 192.168.1.2, the value will be 192.168.1.1). If no HTTP_X_FORWARDED_FOR header is present in the environ at all, this attribute will return the value of the REMOTE_ADDR header. If the REMOTE_ADDR header is unset, this attribute will return the value None. It is possible for user agents to put someone elses IP or just any string in HTTP_X_FORWARDED_FOR as it is a normal HTTP header. Forward proxies can also provide incorrect values (private IP addresses etc). You cannot blindly trust the result of this method to provide you with valid data unless youre certain that HTTP_X_FORWARDED_FOR has the correct values. The WSGI server must be behind a trusted proxy for this to be true. content_length Gets and sets the Content-Length header (HTTP spec section 14.13). Converts it using int. content_type Return the content type, but leaving off any parameters (like charset, but also things like the type in application/atom+xml; type=entry) If you set this property, you can include parameters, or if you dont include any parameters in the value then existing parameters will be preserved. 643
53. PYRAMID.REQUEST cookies Return a dictionary of cookies as found in the request. copy() Copy the request and environment object. This only does a shallow copy, except of wsgi.input copy_body() Copies the body, in cases where it might be shared with another request object and that is not desired. This copies the body in-place, either into a BytesIO object or a temporary le. copy_get() Copies the request and environment object, but turning this request into a GET along the way. If this was a POST request (or any other verb) then it becomes GET, and the request body is thrown away. date Gets and sets the Date header (HTTP spec section 14.8). Converts it using HTTP date. classmethod from_bytes(b) Create a request from HTTP bytes data. If the bytes contain extra data after the request, raise a ValueError. classmethod from_file(fp) Read a request from a le-like object (it must implement .read(size) and .readline()). It will read up to the end of the request, not the end of the le (unless the request is a POST or PUT and has no Content-Length, in that case, the entire le is read). This reads the request as represented by str(req); it may not read every valid HTTP request properly. get_response(application=None, catch_exc_info=False) Like .call_application(application), except returns a response object with .status, .headers, and .body attributes. This will use self.ResponseClass to gure out the class of the response object to return. If application is not given, self.make_default_send_app() 644 this will send the request to
headers All the request headers as a case-insensitive dictionary-like object. host Host name provided in HTTP_HOST, with fall-back to SERVER_NAME host_port The effective server port number as a string. If the HTTP_HOST header exists in the WSGI environ, this attribute returns the port number present in that header. If the HTTP_HOST header exists but contains no explicit port number: if the WSGI url scheme is https , this attribute returns 443, if the WSGI url scheme is http, this attribute returns 80 . If no HTTP_HOST header is present in the environ at all, this attribute will return the value of the SERVER_PORT header (which is guaranteed to be present). host_url The URL through the host (no path) http_version Gets and sets the SERVER_PROTOCOL key in the environment. if_match Gets and sets the If-Match header (HTTP spec section 14.24). Converts it as a Etag. if_modified_since Gets and sets the If-Modified-Since header (HTTP spec section 14.25). Converts it using HTTP date. if_none_match Gets and sets the If-None-Match header (HTTP spec section 14.26). Converts it as a Etag. if_range Gets and sets the If-Range header (HTTP spec section 14.27). Converts it using IfRange object. if_unmodified_since Gets and sets the If-Unmodified-Since header (HTTP spec section 14.28). Converts it using HTTP date. is_body_readable webob.is_body_readable is a ag that tells us that we can read the input stream even though CONTENT_LENGTH is missing. This allows FakeCGIBody to work and can be used by servers to support chunked encoding in requests. For background see https://bitbucket.org/ianb/webob/issue/6 645
53. PYRAMID.REQUEST is_body_seekable Gets and sets the webob.is_body_seekable key in the environment. is_response(ob) Return True if the object passed as ob is a valid response object, False otherwise. is_xhr Is X-Requested-With header present and equal to XMLHttpRequest? Note: this isnt set by every XMLHttpRequest request, it is only set if you are using a Javascript library that sets it (or you set the header yourself manually). Currently Prototype and jQuery are known to set this header. json Access the body of the request as JSON make_body_seekable() This forces environ[wsgi.input] to be seekable. That means that, the content is copied into a BytesIO or temporary le and agged as seekable, so that it will not be unnecessarily copied again. After calling this method the .body_le is always seeked to the start of le and .content_length is not None. The choice to copy to BytesIO is made from self.request_body_tempfile_limit make_tempfile() Create a temple to store big request body. This API is not stable yet. A size argument might be added. max_forwards Gets and sets the Max-Forwards header (HTTP spec section 14.31). Converts it using int. method Gets and sets the REQUEST_METHOD key in the environment. params A dictionary-like object containing both the parameters from the query string and request body. path The path of the request, without host or query string
646
path_info Gets and sets the PATH_INFO key in the environment. path_info_peek() Returns the next segment on PATH_INFO, or None if there is no next segment. Doesnt modify the environment. path_info_pop(pattern=None) Pops off the next segment of PATH_INFO, pushing it onto SCRIPT_NAME, and returning the popped segment. Returns None if there is nothing left on PATH_INFO. Does not return when theres an empty segment (like /path//path); these segments are just ignored. Optional pattern argument is a regexp to match the return value before returning. If there is no match, no changes are made to the request and None is returned. path_qs The path of the request, without host but with query string path_url The URL including SCRIPT_NAME and PATH_INFO, but not QUERY_STRING pragma Gets and sets the Pragma header (HTTP spec section 14.32). query_string Gets and sets the QUERY_STRING key in the environment. range Gets and sets the Range header (HTTP spec section 14.35). Converts it using Range object. referer Gets and sets the Referer header (HTTP spec section 14.36). referrer Gets and sets the Referer header (HTTP spec section 14.36). relative_url(other_url, to_application=False) Resolve other_url relative to the request URL. If to_application is True, then resolve it relative to the URL with only SCRIPT_NAME
647
53. PYRAMID.REQUEST remote_addr Gets and sets the REMOTE_ADDR key in the environment. remote_user Gets and sets the REMOTE_USER key in the environment. remove_conditional_headers(remove_encoding=True, remove_range=True, remove_match=True, remove_modied=True) Remove headers that make the request conditional. These headers can cause the response to be 304 Not Modied, which in some cases you may not want to be possible. This does not remove headers like If-Match, which are used for conict detection. resource_path(resource, *elements, **kw) Generates a path (aka a relative URL, a URL minus the host, scheme, and port) for a resource. This function accepts the same argument as pyramid.request.Request.resource_url() and performs the same duty. It just omits the host, port, and scheme information in the return value; only the script_name, path, query parameters, and anchor data are present in the returned string.
Calling request.resource_path(resource) is the same as calling request.resource_path(resource, app_url=request.script_name). pyramid.request.Request.resource_path() is, in fact, implemented in terms of pyramid.request.Request.resource_url() in just this way. As a result, any app_url passed within the **kw values to route_path will be ignored. scheme, host, and port are also ignored. response This attribute is actually a reied property which returns an instance of the pyramid.response.Response. class. The response object returned does not exist until this attribute is accessed. Once it is accessed, subsequent accesses will return the same Response object. The request.response API is used by renderers. A render obtains the response object it will return from a view that uses that renderer by accessing request.response. Therefore, its possible to use the request.response API to set up a response object with the right attributes (e.g. by calling request.response.set_cookie()) within a view that uses a renderer. Mutations to this response object will be preserved in the response sent to the client. 648
scheme Gets and sets the wsgi.url_scheme key in the environment. script_name Gets and sets the SCRIPT_NAME key in the environment. send(application=None, catch_exc_info=False) Like .call_application(application), except returns a response object with .status, .headers, and .body attributes. This will use self.ResponseClass to gure out the class of the response object to return. If application is not given, self.make_default_send_app() this will send the request to
server_name Gets and sets the SERVER_NAME key in the environment. server_port Gets and sets the SERVER_PORT key in the environment. Converts it using int. session Obtain the session object associated with this request. sion factory has not been registered during application pyramid.exceptions.ConfigurationError will be raised str_GET <Deprecated attribute None> str_POST <Deprecated attribute None> str_cookies <Deprecated attribute None> str_params <Deprecated attribute None> text Get/set the text value of the body upath_info Gets and sets the PATH_INFO key in the environment. 649
If a sesconguration, a
53. PYRAMID.REQUEST url The full request URL, including QUERY_STRING url_encoding Gets and sets the webob.url_encoding key in the environment. urlargs Return any positional variables matched in the URL. Takes values from environ[wsgiorg.routing_args]. Systems like routes set this value. urlvars Return any named variables matched in the URL. Takes values from environ[wsgiorg.routing_args]. Systems like routes set this value. uscript_name Gets and sets the SCRIPT_NAME key in the environment. user_agent Gets and sets the User-Agent header (HTTP spec section 14.43).
For information about the API of a multidict structure (such as that used as request.GET, request.POST, and request.params), see pyramid.interfaces.IMultiDict.
650
CHAPTER
FIFTYFOUR
PYRAMID.RESPONSE
class Response(body=None, status=None, headerlist=None, app_iter=None, tent_type=None, conditional_response=None, **kw) accept_ranges Gets and sets the Accept-Ranges header (HTTP spec section 14.5). age Gets and sets the Age header (HTTP spec section 14.6). Converts it using int. allow Gets and sets the Allow header (HTTP spec section 14.7). Converts it using list. app_iter Returns the app_iter of the response. If body was set, this will create an app_iter from that body (a single-item list) app_iter_range(start, stop) Return a new app_iter built from the response app_iter, that serves up only the given start:stop range. body The body of the response, as a str. This will read in the entire app_iter if necessary. body_file A le-like object that can be used to write to the body. If you passed in a list app_iter, that app_iter will be modied by writes. 651 con-
54. PYRAMID.RESPONSE cache_control Get/set/modify the Cache-Control header (HTTP spec section 14.9) charset Get/set the charset (in the Content-Type) conditional_response_app(environ, start_response) Like the normal __call__ interface, but checks conditional headers: If-Modied-Since (304 Not Modied; only on GET, HEAD) If-None-Match (304 Not Modied; only on GET, HEAD) Range (406 Partial Content; only on GET, HEAD) content_disposition Gets and sets the Content-Disposition header (HTTP spec section 19.5.1). content_encoding Gets and sets the Content-Encoding header (HTTP spec section 14.11). content_language Gets and sets the Content-Language header (HTTP spec section 14.12). Converts it using list. content_length Gets and sets the Content-Length header (HTTP spec section 14.17). Converts it using int. content_location Gets and sets the Content-Location header (HTTP spec section 14.14). content_md5 Gets and sets the Content-MD5 header (HTTP spec section 14.14). content_range Gets and sets the Content-Range header (HTTP spec section 14.16). Converts it using ContentRange object. content_type Get/set the Content-Type header (or None), without the charset or any parameters. If you include parameters (or ; at all) when setting the content_type, any existing parameters will be deleted; otherwise they will be preserved. 652
content_type_params A dictionary of all the parameters in the content type. (This is not a view, set to change, modications of the dict would not be applied otherwise) copy() Makes a copy of the response date Gets and sets the Date header (HTTP spec section 14.18). Converts it using HTTP date. delete_cookie(key, path=/, domain=None) Delete a cookie from the client. Note that path and domain must match how the cookie was originally set. This sets the cookie to the empty string, and max_age=0 so that it should expire immediately. encode_content(encoding=gzip, lazy=False) Encode the content with the given encoding (only gzip and identity are supported). etag Gets and sets the ETag header (HTTP spec section 14.19). Converts it using Entity tag. expires Gets and sets the Expires header (HTTP spec section 14.21). Converts it using HTTP date. classmethod from_file(fp) Reads a response from a le-like object (it must implement .read(size) and .readline()). It will read up to the end of the response, not the end of the le. This reads the response as represented by str(resp); it may not read every valid HTTP response properly. Responses must have a Content-Length headerlist The list of response headers headers The headers in a dictionary-like object json Access the body of the response as JSON 653
54. PYRAMID.RESPONSE json_body Access the body of the response as JSON last_modified Gets and sets the Last-Modified header (HTTP spec section 14.29). Converts it using HTTP date. location Gets and sets the Location header (HTTP spec section 14.30). md5_etag(body=None, set_content_md5=False) Generate an etag for the response object using an MD5 hash of the body (the body parameter, or self.body if not given) Sets self.etag If set_content_md5 is True sets self.content_md5 as well merge_cookies(resp) Merge the cookies that were set on this response with the given resp object (which can be any WSGI application). If the resp is a webob.Response object, then the other object will be modied in-place. pragma Gets and sets the Pragma header (HTTP spec section 14.32). retry_after Gets and sets the Retry-After header (HTTP spec section 14.37). Converts it using HTTP date or delta seconds. server Gets and sets the Server header (HTTP spec section 14.38). set_cookie(key, value=, max_age=None, path=/, domain=None, secure=False, httponly=False, comment=None, expires=None, overwrite=False) Set (add) a cookie for the response. Arguments are: key The cookie name. value 654
The cookie value, which should be a string or None. If value is None, its equivalent to calling the webob.response.Response.unset_cookie() method for this cookie key (it effectively deletes the cookie on the client). max_age An integer representing a number of seconds or None. If this value is an integer, it is used as the Max-Age of the generated cookie. If expires is not passed and this value is an integer, the max_age value will also inuence the Expires value of the cookie (Expires will be set to now + max_age). If this value is None, the cookie will not have a Max-Age value (unless expires is also sent). path A string representing the cookie Path value. It defaults to /. domain A string representing the cookie Domain, or None. If domain is None, no Domain value will be sent in the cookie. secure A boolean. If its True, the secure ag will be sent in the cookie, if its False, the secure ag will not be sent in the cookie. httponly A boolean. If its True, the HttpOnly ag will be sent in the cookie, if its False, the HttpOnly ag will not be sent in the cookie. comment A string representing the cookie Comment value, or None. If comment is None, no Comment value will be sent in the cookie. expires A datetime.timedelta object representing an amount of time or the value None. A non-None value is used to generate the Expires value of the generated cookie. If max_age is not passed, but this value is not None, it will inuence the Max-Age header (Max-Age will be expires_value - datetime.utcnow()). If this value is None, the Expires cookie value will be unset (unless max_age is also passed). 655
54. PYRAMID.RESPONSE overwrite If this key is True, before setting the cookie, unset any existing cookie. status The status string status_code The status as an integer status_int The status as an integer text Get/set the text value of the body (using the charset of the Content-Type) ubody Deprecated alias for .text unicode_body Deprecated alias for .text unset_cookie(key, strict=True) Unset a cookie with the given name (remove it from the response). vary Gets and sets the Vary header (HTTP spec section 14.44). Converts it using list. www_authenticate Gets and sets the WWW-Authenticate header (HTTP spec section 14.47). Converts it using parse_auth and serialize_auth. class FileResponse(path, request=None, cache_max_age=None, content_type=None, content_encoding=None) A Response object that can be used to serve a static le from disk simply. path is a le path on disk. request must be a Pyramid request object if passed. Note that a request must be passed if the response is meant to attempt to use the wsgi.file_wrapper feature of the web server that youre using to serve your Pyramid application. cache_max_age if passed, is the number of seconds that should be used to HTTP cache this response. content_type, if passed, is the content_type of the response. content_encoding, if passed is the content_encoding of the response. Its generally safe to leave this set to None if youre serving a binary le. This argument will be ignored if you dont also pass content-type. 656
54.1. FUNCTIONS class FileIter(le, block_size=262144) A xed-block-size iterator for use as a WSGI app_iter. file is a Python le pointer (or at least an object with a read method that takes a size hint). block_size is an optional block size for iteration.
54.1 Functions
response_adapter(*types_or_ifaces) Decorator activated via a scan which treats the function being decorated as a response adapter for the set of types or interfaces passed as *types_or_ifaces to the decorator constructor. For example, if you scan the following response adapter:
from pyramid.response import Response from pyramid.response import response_adapter @response_adapter(int) def myadapter(i): return Response(status=i)
You can then return an integer from your view callables, and it will be converted into a response with the integer as the status code. More than one type or interface can be passed as a constructor argument. The decorated response adapter will be called for each type or interface.
import json from pyramid.response import Response from pyramid.response import response_adapter @response_adapter(dict, list) def myadapter(ob): return Response(json.dumps(ob))
This method will have no effect until a scan is performed agains the package or module which contains it, ala:
657
54. PYRAMID.RESPONSE
658
CHAPTER
FIFTYFIVE
PYRAMID.SCRIPTING
get_root(app, request=None) Return a tuple composed of (root, closer) when provided a router instance as the app argument. The root returned is the application root object. The closer returned is a callable (accepting no arguments) that should be called when your scripting application is nished using the root. request is passed to the Pyramid application root factory to compute the root. If request is None, a default will be constructed using the registrys Request Factory via the pyramid.interfaces.IRequestFactory.blank() method. prepare(request=None, registry=None) This function pushes data onto the Pyramid threadlocal stack (request and registry), making those objects current. It returns a dictionary useful for bootstrapping a Pyramid application in a scripting environment. request is passed to the Pyramid application root factory to compute the root. If request is None, a default will be constructed using the registrys Request Factory via the pyramid.interfaces.IRequestFactory.blank() method. If registry is not supplied, the last registry loaded from pyramid.config.global_registries will be used. If you have loaded more than one Pyramid application in the current process, you may not want to use the last registry loaded, thus you can search the global_registries and supply the appropriate one based on your own criteria. The function returns a dictionary composed of root, closer, registry, request and root_factory. The root returned is the applications root resource object. The closer returned is a callable (accepting no arguments) that should be called when your scripting application is nished using the root. registry is the registry object passed or the last registry loaded into pyramid.config.global_registries if no registry is passed. request is the request object passed or the constructed request if no request is passed. root_factory is the root factory used to construct the root. 659
55. PYRAMID.SCRIPTING
660
CHAPTER
FIFTYSIX
PYRAMID.SECURITY
56.1 Authentication API Functions
authenticated_userid(request) Return the userid of the currently authenticated user or None if there is no authentication policy in effect or there is no currently authenticated user. unauthenticated_userid(request) Return an object which represents the claimed (not veried) user id of the credentials present in the request. None if there is no authentication policy in effect or there is no user data associated with the current request. This differs from authenticated_userid(), because the effective authentication policy will not ensure that a record associated with the userid exists in persistent storage. effective_principals(request) Return the list of effective principal identiers for the request. This will include the userid of the currently authenticated user if a user is currently authenticated. If no authentication policy is in effect, this will return an empty sequence. forget(request) Return a sequence of header tuples (e.g. [(Set-Cookie, foo=abc)]) suitable for forgetting the set of credentials possessed by the currently authenticated user. A common usage might look like so within the body of a view function (response is assumed to be an WebOb -style response object computed previously by the view code):
661
56. PYRAMID.SECURITY
If no authentication policy is in use, this function will always return an empty sequence. remember(request, principal, **kw) Return a sequence of header tuples (e.g. [(Set-Cookie, foo=abc)]) suitable for remembering a set of credentials implied by the data passed as principal and *kw using the current authentication policy. Common usage might look like so within the body of a view function (response is assumed to be a WebOb -style response object computed previously by the view code):
from pyramid.security import remember headers = remember(request, chrism, password=123, max_age=86400) response.headerlist.extend(headers) return response
If no authentication policy is in use, this function will always return an empty sequence. If used, the composition and meaning of **kw must be agreed upon by the calling code and the effective authentication policy.
662
56.3. CONSTANTS
even if an authorization policy is in effect, some (exotic) authorization policies may not implement the required machinery for this function; those will cause a NotImplementedError exception to be raised when this function is invoked.
view_execution_permitted(context, request, name=) If the view specied by context and name is protected by a permission, check the permission associated with the view using the effective authentication/authorization policies and the request. Return a boolean result. If no authorization policy is in effect, or if the view is not protected by a permission, return True. If no view can view found, an exception will be raised. Changed in version 1.4a4: An exception is raised if no view is found.
56.3 Constants
Everyone The special principal id named Everyone. This principal id is granted to all requests. Its actual value is the string system.Everyone. Authenticated The special principal id named Authenticated. This principal id is granted to all requests which contain any other non-Everyone principal id (according to the authentication policy). Its actual value is the string system.Authenticated. ALL_PERMISSIONS An object that can be used as the permission member of an ACE which matches all permissions unconditionally. For example, an ACE that uses ALL_PERMISSIONS might be composed like so: (Deny, system.Everyone, ALL_PERMISSIONS). DENY_ALL A convenience shorthand ACE that denes (Deny, system.Everyone, ALL_PERMISSIONS). This is often used as the last ACE in an ACL in systems that use an inheriting security policy, representing the concept dont inherit any other ACEs. NO_PERMISSION_REQUIRED A special permission which indicates that the view should always be executable by entirely anonymous users, regardless of the default permission, bypassing any authorization policy that may be in effect. Its actual value is the string __no_permission_required__.
663
56. PYRAMID.SECURITY
664
CHAPTER
FIFTYSEVEN
PYRAMID.SETTINGS
asbool(s) Return the boolean value True if the case-lowered value of string input s is any of t, true, y, on, or 1, otherwise return the boolean value False. If s is the value None, return False. If s is already one of the boolean values True or False, return it. aslist(value, atten=True) Return a list of strings, separating the input based on newlines and, if atten=True (the default), also split on spaces within each line.
665
57. PYRAMID.SETTINGS
666
CHAPTER
FIFTYEIGHT
PYRAMID.TESTING
setUp(registry=None, request=None, hook_zca=True, autocommit=True, settings=None) Set Pyramid registry and request thread locals for the duration of a single unit test. Use this function in the setUp method of a unittest test case which directly or indirectly uses: any method of the pyramid.config.Configurator object returned by this function. the pyramid.threadlocal.get_current_registry() pyramid.threadlocal.get_current_request() functions. or
If you use the get_current_* functions (or call Pyramid code that uses these functions) without calling setUp, pyramid.threadlocal.get_current_registry() will return a global application registry, which may cause unit tests to not be isolated with respect to registrations they perform. If the registry argument is None, a new empty application registry will be created (an instance of the pyramid.registry.Registry class). If the registry argument is not None, the value passed in should be an instance of the pyramid.registry.Registry class or a suitable testing analogue. After setUp is nished, the registry returned pyramid.threadlocal.get_current_request() function will be (or constructed) registry until pyramid.testing.tearDown() is pyramid.testing.setUp() is called again) . If the form hook_zca argument the operation by the the passed called (or
58. PYRAMID.TESTING pyramid.threadlocal.get_current_registry), which will cause the Zope Component Architecture global API (e.g. zope.component.getSiteManager(), zope.component.getAdapter(), and so on) to use the registry constructed by setUp as the value it returns from zope.component.getSiteManager(). If the zope.component package cannot be imported, or if hook_zca is False, the hook will not be set. If settings is not None, it must be a dictionary representing the values passed to a Congurator as its settings= argument. This function returns an instance of the pyramid.config.Configurator class, which can be used for further conguration to set up an environment suitable for a unit or integration test. The registry attribute attached to the Congurator instance represents the current application registry; the same registry will be returned by pyramid.threadlocal.get_current_registry() during the execution of the test. tearDown(unhook_zca=True) Undo the effects pyramid.testing.setUp(). Use this function in the tearDown method of a unit test that uses pyramid.testing.setUp() in its setUp method. If the unhook_zca argument is True (the default), call zope.component.getSiteManager.reset(). This undoes the action of pyramid.testing.setUp() called with the argument hook_zca=True. If zope.component cannot be imported, ignore the argument. testConfig(registry=None, request=None, tings=None) Returns a context manager for test set up. hook_zca=True, autocommit=True, set-
This context manager calls pyramid.testing.testSetup() when entering and pyramid.testing.tearDown() when exiting. All arguments are passed directly to pyramid.testing.testSetup(). If the ZCA is hooked, it will always be un-hooked in tearDown. This context manager allows you to write test code like this:
1 2 3 4
class DummyResource(__name__=None, __parent__=None, __provides__=None, **kw) A dummy Pyramid resource object. clone(__name__=<object object at 0x6fa8d50>, __parent__=<object object at 0x6fa8d50>, **kw) Create a clone of the resource object. If __name__ or __parent__ arguments are passed, use these values to override the existing __name__ or __parent__ of the resource. If any extra keyword args are passed in via the kw argument, use these keywords to add to or override existing resource keywords (attributes). items() Return the items set by __setitem__ keys() Return the keys set by __setitem__ values() Return the values set by __setitem__ class DummyRequest(params=None, environ=None, headers=None, path=/, cookies=None, post=None, **kw) A DummyRequest object (incompletely) imitates a request object. The params, environ, headers, path, and cookies arguments correspond to their WebOb equivalents. The post argument, if passed, populates the requests POST attribute, but not params, in order to allow testing that the app accepts data for a given view only from POST requests. This argument also sets self.method to POST. Extra keyword arguments are assigned as attributes of the request itself. Note that DummyRequest does not have complete delity with a real request. For example, by default, the DummyRequest GET and POST attributes are of type dict, unlike a normal Requests GET and POST, which are of type MultiDict. If your code uses the features of MultiDict, you should either use a real pyramid.request.Request or adapt your DummyRequest by replacing the attributes with MultiDict instances. Other similar incompatibilities exist. If you need all the features of a Request, use the pyramid.request.Request class itself rather than this class while writing tests.
class DummyTemplateRenderer(string_response=) An instance of this class is returned from pyramid.config.Configurator.testing_add_renderer It has a helper function (assert_) that makes it possible to make an assertion which compares data passed to the renderer by the view function against expected key/value pairs. 669
58. PYRAMID.TESTING assert_(**kw) Accept an arbitrary set of assertion key/value pairs. For each assertion key/value pair assert that the renderer (eg. pyramid.renderer.render_to_response()) received the key with a value that equals the asserted value. If the renderer did not receive the key at all, or the value received by the renderer doesnt match the assertion value, raise an AssertionError.
670
CHAPTER
FIFTYNINE
PYRAMID.THREADLOCAL
get_current_request() Return the currently active request or None if no request is currently active. This function should be used extremely sparingly, usually only in unit testing code. its almost always usually a mistake to use get_current_request outside a testing context because its usage makes it possible to write code that can be neither easily tested nor scripted. get_current_registry() Return the currently active application registry or the global application registry if no request is currently active. This function should be used extremely sparingly, usually only in unit testing code. its almost always usually a mistake to use get_current_registry outside a testing context because its usage makes it possible to write code that can be neither easily tested nor scripted.
671
59. PYRAMID.THREADLOCAL
672
CHAPTER
SIXTY
PYRAMID.TRAVERSAL
find_interface(resource, class_or_interface) Return the rst resource found in the lineage of resource which, a) if class_or_interface is a Python class object, is an instance of the class or any subclass of that class or b) if class_or_interface is a interface, provides the specied interface. Return None if no resource providing interface_or_class can be found in the lineage. The resource passed in must be location-aware. find_resource(resource, path) Given a resource object and a string or tuple representing a path (such as the return value of pyramid.traversal.resource_path() or pyramid.traversal.resource_path_tuple()), return a resource in this applications resource tree at the specied path. The resource passed in must be location-aware. If the path cannot be resolved (if the respective node in the resource tree does not exist), a KeyError will be raised. This function is the logical inverse of pyramid.traversal.resource_path() and pyramid.traversal.resource_path_tuple(); it can resolve any path string or tuple generated by either of those functions. Rules for passing a string as the path argument: if the rst character in the path string is the / character, the path is considered absolute and the resource tree traversal will start at the root resource. If the rst character of the path string is not the / character, the path is considered relative and resource tree traversal will begin at the resource object supplied to the function as the resource argument. If an empty string is passed as path, the resource passed in will be returned. Resource path strings must be escaped in the following manner: each Unicode path segment must be encoded as UTF-8 and as each path segment must escaped via Pythons urllib.quote. For example,
673
60. PYRAMID.TRAVERSAL /path/to%20the/La%20Pe%C3%B1a (absolute) or to%20the/La%20Pe%C3%B1a (relative). The pyramid.traversal.resource_path() function generates strings which follow these rules (albeit only absolute ones). Rules for passing text (Unicode) as the path argument are the same as those for a string. In particular, the text may not have any nonascii characters in it. Rules for passing a tuple as the path argument: if the rst element in the path tuple is the empty string (for example (, a, b, c), the path is considered absolute and the resource tree traversal will start at the resource tree root object. If the rst element in the path tuple is not the empty string (for example (a, b, c)), the path is considered relative and resource tree traversal will begin at the resource object supplied to the function as the resource argument. If an empty sequence is passed as path, the resource passed in itself will be returned. No URL-quoting or UTF-8-encoding of individual path segments within the tuple is required (each segment may be any string or unicode object representing a resource name). Resource path tuples generated by pyramid.traversal.resource_path_tuple() can always be resolved by find_resource.
For backwards compatibility purposes, this function can also be imported as pyramid.traversal.find_model(), although doing so will emit a deprecation warning.
find_root(resource) Find the root node in the resource tree to which resource belongs. Note that resource should be location-aware. Note that the root resource is available in the request object by accessing the request.root attribute. resource_path(resource, *elements) Return a string object representing the absolute physical path of the resource object based on its position in the resource tree, e.g /foo/bar. Any positional arguments passed in as elements will be appended as path segments to the end of the resource path. For instance, if the resources path is /foo/bar and elements equals (a, b), the returned string will be /foo/bar/a/b. The rst character in the string will always be the / character (a leading / character in a path string represents that the path is absolute). Resource path strings returned will be escaped in the following manner: each unicode path segment will be encoded as UTF-8 and each path segment will be escaped via Pythons urllib.quote. For example, /path/to%20the/La%20Pe%C3%B1a. This function is a logical inverse of pyramid.traversal.find_resource: it can be used to generate path references that can later be resolved via that function. 674
Each segment in the path string returned will use the __name__ attribute of the resource it represents within the resource tree. Each of these segments should be a unicode or string object (as per the contract of location-awareness). However, no conversion or safety checking of resource names is performed. For instance, if one of the resources in your tree has a __name__ which (by error) is a dictionary, the pyramid.traversal.resource_path() function will attempt to append it to a string and it will cause a pyramid.exceptions.URLDecodeError.
The root resource must have a __name__ attribute with a value of either None or the empty string for paths to be generated properly. If the root resource has a non-null __name__ attribute, its name will be prepended to the generated path rather than a single leading / character.
For backwards compatibility purposes, this function can also be imported as model_path, although doing so will cause a deprecation warning to be emitted. resource_path_tuple(resource, *elements) Return a tuple representing the absolute physical path of the resource object based on its position in a resource tree, e.g (, foo, bar). Any positional arguments passed in as elements will be appended as elements in the tuple representing the resource path. For instance, if the resources path is (, foo, bar) and elements equals (a, b), the returned tuple will be (, foo, bar, a, b). The rst element of this tuple will always be the empty string (a leading empty string element in a path tuple represents that the path is absolute). This function is a logical inverse of pyramid.traversal.find_resource(): it can be used to generate path references that can later be resolved by that function. The resource passed in must be location-aware.
Each segment in the path tuple returned will equal the __name__ attribute of the resource it represents within the resource tree. Each of these segments should be a unicode or string object (as per the contract of location-awareness). However, no conversion or safety checking of resource names is performed. For instance, if one of the resources in your tree has a __name__ which (by error) is a dictionary, that dictionary will be placed in the path tuple; no warning or error will be given.
675
60. PYRAMID.TRAVERSAL
The root resource must have a __name__ attribute with a value of either None or the empty string for path tuples to be generated properly. If the root resource has a non-null __name__ attribute, its name will be the rst element in the generated path tuple rather than the empty string.
For backwards compatibility purposes, this function can also be imported as model_path_tuple, although doing so will cause a deprecation warning to be emitted.
quote_path_segment(segment, safe=) virtual_root(resource, request) Provided any resource and a request object, return the resource object representing the virtual root of the current request. Using a virtual root in a traversal -based Pyramid application permits rooting, for example, the resource at the traversal path /cms at http://example.com/ instead of rooting it at http://example.com/cms/. If the resource passed in is a context obtained via traversal, and if the HTTP_X_VHM_ROOT key is in the WSGI environment, the value of this key will be treated as a virtual root path: the pyramid.traversal.find_resource() API will be used to nd the virtual root resource using this path; if the resource is found, it will be returned. If the HTTP_X_VHM_ROOT key is not present in the WSGI environment, the physical root of the resource tree will be returned instead. Virtual roots are not useful at all in applications that use URL dispatch. Contexts obtained via URL dispatch dont really support being virtually rooted (each URL dispatch context is both its own physical and virtual root). However if this API is called with a resource argument which is a context obtained via URL dispatch, the resource passed in will be returned unconditionally. traverse(resource, path) Given a resource object as resource and a string or tuple representing a path as path (such as the return value of pyramid.traversal.resource_path() or pyramid.traversal.resource_path_tuple() or the value of request.environ[PATH_INFO]), return a dictionary with the keys context, root, view_name, subpath, traversed, virtual_root, and virtual_root_path. A denition of each value in the returned dictionary: context: The context (a resource object) found via traversal or url dispatch. If the path passed in is the empty string, the value of the resource argument passed to this function is returned. 676
root: The resource object at which traversal begins. If the resource passed in was found via url dispatch or if the path passed in was relative (non-absolute), the value of the resource argument passed to this function is returned. view_name: The view name found during traversal or url dispatch; if the resource was found via traversal, this is usually a representation of the path segment which directly follows the path to the context in the path. The view_name will be a Unicode object or the empty string. The view_name will be the empty string if there is no element which follows the context path. An example: if the path passed is /foo/bar, and a resource object is found at /foo (but not at /foo/bar), the view name will be ubar. If the resource was found via urldispatch, the view_name will be the name the route found was registered with. subpath: For a resource found via traversal, this is a sequence of path segments found in the path that follow the view_name (if any). Each of these items is a Unicode object. If no path segments follow the view_name, the subpath will be the empty sequence. An example: if the path passed is /foo/bar/baz/buz, and a resource object is found at /foo (but not /foo/bar), the view name will be ubar and the subpath will be [ubaz, ubuz]. For a resource found via url dispatch, the subpath will be a sequence of values discerned from *subpath in the route pattern matched or the empty sequence. traversed: The sequence of path elements traversed from the root to nd the context object during traversal. Each of these items is a Unicode object. If no path segments were traversed to nd the context object (e.g. if the path provided is the empty string), the traversed value will be the empty sequence. If the resource is a resource found via url dispatch, traversed will be None. virtual_root: A resource object representing the virtual root of the resource tree being traversed during traversal. See Virtual Hosting for a denition of the virtual root object. If no virtual hosting is in effect, and the path passed in was absolute, the virtual_root will be the physical root resource object (the object at which traversal begins). If the resource passed in was found via URL dispatch or if the path passed in was relative, the virtual_root will always equal the root object (the resource passed in). virtual_root_path If traversal was used to nd the resource, this will be the sequence of path elements traversed to nd the virtual_root resource. Each of these items is a Unicode object. If no path segments were traversed to nd the virtual_root resource (e.g. if virtual hosting is not in effect), the traversed value will be the empty list. If url dispatch was used to nd the resource, this will be None. If the path cannot be resolved, a KeyError will be raised. Rules for passing a string as the path argument: if the rst character in the path string is the with the / character, the path will considered absolute and the resource tree traversal will start at the root resource. If the rst character of the path string is not the / 677
60. PYRAMID.TRAVERSAL character, the path is considered relative and resource tree traversal will begin at the resource object supplied to the function as the resource argument. If an empty string is passed as path, the resource passed in will be returned. Resource path strings must be escaped in the following manner: each Unicode path segment must be encoded as UTF-8 and each path segment must escaped via Pythons urllib.quote. For example, /path/to%20the/La%20Pe%C3%B1a (absolute) or to%20the/La%20Pe%C3%B1a (relative). The pyramid.traversal.resource_path() function generates strings which follow these rules (albeit only absolute ones). Rules for passing a tuple as the path argument: if the rst element in the path tuple is the empty string (for example (, a, b, c), the path is considered absolute and the resource tree traversal will start at the resource tree root object. If the rst element in the path tuple is not the empty string (for example (a, b, c)), the path is considered relative and resource tree traversal will begin at the resource object supplied to the function as the resource argument. If an empty sequence is passed as path, the resource passed in itself will be returned. No URLquoting or UTF-8-encoding of individual path segments within the tuple is required (each segment may be any string or unicode object representing a resource name). Explanation of the conversion of path segment values to Unicode during traversal: Each segment is URL-unquoted, and decoded into Unicode. Each segment is assumed to be encoded using the UTF-8 encoding (or a subset, such as ASCII); a pyramid.exceptions.URLDecodeError is raised if a segment cannot be decoded. If a segment name is empty or if it is ., it is ignored. If a segment name is .., the previous segment is deleted, and the .. is ignored. As a result of this process, the return values view_name, each element in the subpath, each element in traversed, and each element in the virtual_root_path will be Unicode as opposed to a string, and will be URL-decoded. traversal_path(path) Variant of pyramid.traversal.traversal_path_info() suitable for decoding paths that are URL-encoded. If this function is passed a Unicode object instead of a sequence of bytes as path, that Unicode object must directly encodeable to ASCII. For example, u/foo will work but u/<unprintable unicode> (a Unicode object with characters that cannot be encoded to ascii) will not. A UnicodeEncodeError will be raised if the Unicode cannot be encoded directly to ASCII.
678
CHAPTER
SIXTYONE
PYRAMID.URL
Utility functions for dealing with URLs in pyramid resource_url(context, request, *elements, query=None, anchor=None) This is a backwards compatibility function. Its result is the same as calling:
request.resource_url(resource, *elements, **kw)
See pyramid.request.Request.resource_url() for more information. route_url(route_name, request, *elements, **kw) This is a backwards compatibility function. Its result is the same as calling:
request.route_url(route_name, *elements, **kw)
See pyramid.request.Request.route_url() for more information. current_route_url(request, *elements, **kw) This is a backwards compatibility function. Its result is the same as calling:
request.current_route_url(*elements, **kw)
See pyramid.request.Request.current_route_url() for more information. route_path(route_name, request, *elements, **kw) This is a backwards compatibility function. Its result is the same as calling:
679
61. PYRAMID.URL
See pyramid.request.Request.route_path() for more information. current_route_path(request, *elements, **kw) This is a backwards compatibility function. Its result is the same as calling:
request.current_route_path(*elements, **kw)
See pyramid.request.Request.current_route_path() for more information. static_url(path, request, **kw) This is a backwards compatibility function. Its result is the same as calling:
request.static_url(path, **kw)
See pyramid.request.Request.static_url() for more information. static_path(path, request, **kw) This is a backwards compatibility function. Its result is the same as calling:
request.static_path(path, **kw)
See pyramid.request.Request.static_path() for more information. urlencode(query, doseq=True) An alternate implementation of Pythons stdlib urllib.urlencode function which accepts unicode keys and values within the query dict/sequence; all Unicode keys and values are rst converted to UTF-8 before being used to compose the query string. The value of query must be a sequence of two-tuples representing key/value pairs or an object (often a dictionary) with an .items() method that returns a sequence of two-tuples representing key/value pairs. For minimal calling convention backwards compatibility, this version of urlencode accepts but ignores a second argument conventionally named doseq. The Python stdlib version behaves differently when doseq is False and when a sequence is presented as one of the values. This version always behaves in the doseq=True mode, no matter what the value of the second argument. See the Python stdlib documentation for urllib.urlencode for more information. 680
CHAPTER
SIXTYTWO
PYRAMID.VIEW
render_view_to_response(context, request, name=, secure=True) Call the view callable congured with a view conguration that matches the view name name registered against the specied context and request and return a response object. This function will return None if a corresponding view callable cannot be found (when no view conguration matches the combination of name / context / and request). If secure is True, and the view callable found is protected by a permission, the permission will be checked before calling the view function. If the permission check disallows view execution (based on the current authorization policy), a pyramid.httpexceptions.HTTPForbidden exception will be raised. The exceptions args attribute explains why the view access was disallowed. If secure is False, no permission checking is done. render_view_to_iterable(context, request, name=, secure=True) Call the view callable congured with a view conguration that matches the view name name registered against the specied context and request and return an iterable object which represents the body of a response. This function will return None if a corresponding view callable cannot be found (when no view conguration matches the combination of name / context / and request). Additionally, this function will raise a ValueError if a view function is found and called but the view functions result does not have an app_iter attribute. You can usually get the bytestring representation of the return value of this function by calling b.join(iterable), or just use pyramid.view.render_view() instead. If secure is True, and the view is protected by a permission, the permission will be checked before the view function is invoked. If the permission check disallows view execution (based on the current authentication policy), a pyramid.httpexceptions.HTTPForbidden exception will be raised; its args attribute explains why the view access was disallowed. If secure is False, no permission checking is done. 681
62. PYRAMID.VIEW render_view(context, request, name=, secure=True) Call the view callable congured with a view conguration that matches the view name name registered against the specied context and request and unwind the view responses app_iter (see View Callable Responses) into a single bytestring. This function will return None if a corresponding view callable cannot be found (when no view conguration matches the combination of name / context / and request). Additionally, this function will raise a ValueError if a view function is found and called but the view functions result does not have an app_iter attribute. This function will return None if a corresponding view cannot be found. If secure is True, and the view is protected by a permission, the permission will be checked before the view is invoked. If the permission check disallows view execution (based on the current authorization policy), a pyramid.httpexceptions.HTTPForbidden exception will be raised; its args attribute explains why the view access was disallowed. If secure is False, no permission checking is done. is_response(ob) Return True if ob implements the interface implied by View Callable Responses. False if not.
This function is deprecated as of Pyramid 1.1. New code should not use it. Instead, new code should use the pyramid.request.Request.is_response() method. class view_config(**settings) A function, class or method decorator which allows a developer to create view registrations nearer to a view callable denition than use imperative conguration to do the same. For example, this code in a module views.py:
from resources import MyResource @view_config(name=my_view, context=MyResource, permission=read, route_name=site1) def my_view(context, request): return OK
682
The following keyword arguments are supported to pyramid.view.view_config: context, permission, name, request_type, route_name, request_method, request_param, containment, xhr, accept, header, path_info, custom_predicates, decorator, mapper, http_cache, match_param, csrf_token, physical_path, and predicates. The meanings of these arguments are the same as the arguments passed to pyramid.config.Configurator.add_view(). If any argument is left out, its default will be the equivalent add_view default. An additional keyword argument named _depth is provided for people who wish to reuse this class from another decorator. The default value is 0 and should be specied relative to the view_config invocation. It will be passed in to the venusian attach function as the depth of the callstack when Venusian checks if the decorator is being used in a class or module context. Its not often used, but it can be useful in this circumstance. See the attach function in Venusian for more information. See Adding View Conguration Using the @view_cong Decorator for details about using pyramid.view.view_config. class view_defaults(**settings) A class decorator which, when applied to a class, will provide defaults for all view congurations that use the class. This decorator accepts all the arguments accepted by pyramid.config.view_config, and each has the same meaning. See @view_defaults Class Decorator for more information. class notfound_view_config(**settings) An analogue of pyramid.view.view_config which registers a not found view. The notfound_view_cong constructor accepts most of the same arguments as the constructor of pyramid.view.view_config. It can be used in the same places, and behaves in largely the same way, except it always registers a not found exception view instead of a normal view. Example:
from pyramid.view import notfound_view_config from pyramid.response import Response @notfound_view_config() def notfound(request): return Response(Not found, dude!, status=404 Not Found)
683
62. PYRAMID.VIEW All arguments except append_slash have the same meaning as pyramid.view.view_config() and each predicate argument restricts the set of circumstances under which this notfound view will be invoked. If append_slash is True, when the notfound view is invoked, and the current path info does not end in a slash, the notfound logic will attempt to nd a route that matches the requests path info sufxed with a slash. If such a route exists, Pyramid will issue a redirect to the URL implied by the route; if it does not, Pyramid will return the result of the view callable provided as view, as normal. See Changing the Not Found View for detailed usage information. This class is new as of Pyramid 1.3. class forbidden_view_config(**settings) An analogue of pyramid.view.view_config which registers a forbidden view. The forbidden_view_cong constructor accepts most of the same arguments as the constructor of pyramid.view.view_config. It can be used in the same places, and behaves in largely the same way, except it always registers a forbidden exception view instead of a normal view. Example:
from pyramid.view import forbidden_view_config from pyramid.response import Response @forbidden_view_config() def notfound(request): return Response(You are not allowed, status=401 Unauthorized)
All arguments passed to this function have the same meaning as pyramid.view.view_config() and each predicate argument restricts the set of circumstances under which this notfound view will be invoked. See Changing the Forbidden View for detailed usage information. This class is new as of Pyramid 1.3. class static(root_dir, cache_max_age=3600, package_name=None) Backwards compatibility alias for pyramid.static.static_view; it overrides that class constructor to pass use_subpath=True by default. This class is deprecated as of Pyramid 1.1. Use pyramid.static.static_view instead (probably with a use_subpath=True argument). 684
CHAPTER
SIXTYTHREE
PYRAMID.WSGI
wsgiapp(wrapped) Decorator to turn a WSGI application into a Pyramid view callable. This decorator differs from the pyramid.wsgi.wsgiapp2() decorator inasmuch as xups of PATH_INFO and SCRIPT_NAME within the WSGI environment are not performed before the application is invoked. E.g., the following in a views.py module:
@wsgiapp def hello_world(environ, start_response): body = Hello world start_response(200 OK, [ (Content-Type, text/plain), (Content-Length, len(body)) ] ) return [body]
The wsgiapp decorator will convert the result of the WSGI application to a Response and return it to Pyramid as if the WSGI app were a pyramid view. wsgiapp2(wrapped) Decorator to turn a WSGI application into a Pyramid view callable. This decorator differs from the pyramid.wsgi.wsgiapp() decorator inasmuch as xups of PATH_INFO and SCRIPT_NAME within the WSGI environment are performed before the application is invoked. E.g. the following in a views.py module: 685
63. PYRAMID.WSGI
@wsgiapp2 def hello_world(environ, start_response): body = Hello world start_response(200 OK, [ (Content-Type, text/plain), (Content-Length, len(body)) ] ) return [body]
The wsgiapp2 decorator will convert the result of the WSGI application to a Response and return it to Pyramid as if the WSGI app were a Pyramid view. The SCRIPT_NAME and PATH_INFO values present in the WSGI environment are xed up before the application is invoked. In particular, a new WSGI environment is generated, and the subpath of the request passed to wsgiapp2 is used as the new requests PATH_INFO and everything preceding the subpath is used as the SCRIPT_NAME. The new environment is passed to the downstream WSGI application.
686
Part IV
GLOSSARY
ACE An access control entry. An access control entry is one element in an ACL. An access control entry is a three-tuple that describes three things: an action (one of either Allow or Deny), a principal (a string describing a user or group), and a permission. For example the ACE, (Allow, bob, read) is a member of an ACL that indicates that the principal bob is allowed the permission read against the resource the ACL is attached to. ACL An access control list. An ACL is a sequence of ACE tuples. An ACL is attached to a resource instance. An example of an ACL is [ (Allow, bob, read), (Deny, fred, write)]. If an ACL is attached to a resource instance, and that resource is ndable via the context resource, it will be consulted any active security policy to determine wither a particular request can be fullled given the authentication information in the request. action Represents a pending conguration statement generated by a call to a conguration directive. The set of pending conguration actions are processed when pyramid.config.Configurator.commit() is called. Agendaless Consulting A consulting organization formed by Paul Everitt, Tres Seaver, and Chris McDonough. See also http://agendaless.com . Akhet Akhet is a Pyramid library and demo application with a Pylons-like feel. Its most known for its former application scaffold, which helped users transition from Pylons and those prefering a more Pylons-like API. The scaffold has been retired but the demo plays a similar role. application registry A registry of conguration information consulted by Pyramid while servicing an application. An application registry maps resource types to views, as well as housing other application-specic component registrations. Every Pyramid application has one (and only one) application registry. asset Any le contained within a Python package which is not a Python source code le. asset descriptor An instance representing an asset specication provided by the pyramid.path.AssetResolver.resolve() method. It supports the methods and attributes documented in pyramid.interfaces.IAssetDescriptor. asset specication A colon-delimited identier for an asset. The colon separates a Python package name from a package subpath. For example, the asset specication my.package:static/baz.css identies the le named baz.css in the static subdirectory of the my.package Python package. See Understanding Asset Specications for more info. 689
63. GLOSSARY authentication The act of determining that the credentials a user presents during a particular request are good. Authentication in Pyramid is performed via an authentication policy. authentication policy An authentication policy in Pyramid terms is a bit of code which has an API which determines the current principal (or principals) associated with a request. authorization The act of determining whether a user can perform a specic action. In pyramid terms, this means determining whether, for a given resource, any principal (or principals) associated with the request have the requisite permission to allow the request to continue. Authorization in Pyramid is performed via its authorization policy. authorization policy An authorization policy in Pyramid terms is a bit of code which has an API which determines whether or not the principals associated with the request can perform an action associated with a permission, based on the information found on the context resource. Babel A collection of tools for internationalizing Python applications. Pyramid does not depend on Babel to operate, but if Babel is installed, additional locale functionality becomes available to your application. Chameleon chameleon is an attribute language template compiler which supports the ZPT templating specication. It is written and maintained by Malthe Borch. It has several extensions, such as the ability to use bracketed (Mako-style) ${name} syntax. It is also much faster than the reference implementation of ZPT. Pyramid offers Chameleon templating out of the box in ZPT and text avors. conguration declaration An individual method call made to a conguration directive, such as registering a view conguration (via the add_view() method of the congurator) or route conguration (via the add_route() method of the congurator). A set of conguration declarations is also implied by the conguration decoration detected by a scan of code in a package. conguration decoration Metadata implying one or more conguration declaration invocations. Often set by conguration Python decorator attributes, such as pyramid.view.view_config, aka @view_config. conguration directive A method of the Congurator which causes a conguration action to occur. The method pyramid.config.Configurator.add_view() is a conguration directive, and application developers can add their own directives as necessary (see Adding Methods to the Congurator via add_directive). congurator An object used to do conguration declaration within an application. The most common congurator is an instance of the pyramid.config.Configurator class.
690
conict resolution Pyramid attempts to resolve ambiguous conguration statements made by application developers via automatic conict resolution. Automatic conict resolution is described in Automatic Conict Resolution. If Pyramid cannot resolve ambiguous conguration statements, it is possible to manually resolve them as described in Manually Resolving Conicts. console script A script written to the bin (on UNIX, or Scripts on Windows) directory of a Python installation or virtualenv as the result of running setup.py install or setup.py develop. context A resource in the resource tree that is found during traversal or URL dispatch based on URL data; if its found via traversal, its usually a resource object that is part of a resource tree; if its found via URL dispatch, its an object manufactured on behalf of the routes factory. A context resource becomes the subject of a view, and often has security information attached to it. See the Traversal chapter and the URL Dispatch chapter for more information about how a URL is resolved to a context resource. CPython The C implementation of the Python language. This is the reference implementation that most people refer to as simply Python; Jython, Googles App Engine, and PyPy are examples of non-C based Python implementations. declarative conguration The conguration mode in which you use the combination of conguration decoration and a scan to congure your Pyramid application. decorator A wrapper around a Python function or class which accepts the function or class as its rst argument and which returns an arbitrary object. Pyramid provides several decorators, used for conguration and return value modication purposes. See also PEP 318. Default Locale Name The locale name used by an application when no explicit locale name is set. See Localization-Related Deployment Settings. default permission A permission which is registered as the default for an entire application. When a default permission is in effect, every view conguration registered with the system will be effectively amended with a permission argument that will require that the executing user possess the default permission in order to successfully execute the associated view callable See also Setting a Default Permission. Default view The default view of a resource is the view invoked when the view name is the empty string (). This is the case when traversal exhausts the path elements in the PATH_INFO of a request before it returns a context resource. Deployment settings Deployment settings are settings passed to the Congurator as a settings argument. These are later accessible via a request.registry.settings dictionary in views or as config.registry.settings in conguration code. Deployment settings can be used as global application values. 691
63. GLOSSARY discriminator The unique identier of an action. distribute Distribute is a fork of setuptools which runs on both Python 2 and Python 3. distribution (Setuptools/distutils terminology). A le representing an installable library or application. Distributions are usually les that have the sufx of .egg, .tar.gz, or .zip. Distributions are the target of Setuptools-related commands such as easy_install. distutils The standard system for packaging and distributing Python packages. See http://docs.python.org/distutils/index.html for more information. setuptools is actually an extension of the Distutils. Django A full-featured Python web framework. domain model Persistent data related to your application. For example, data stored in a relational database. In some applications, the resource tree acts as the domain model. dotted Python name A reference to a Python object by name using a string, in the form path.to.modulename:attributename. Often used in Pyramid and setuptools congurations. A variant is used in dotted names within congurator method arguments that name objects (such as the add_view methods view and context attributes): the colon (:) is not used; in its place is a dot. entry point A setuptools indirection, dened within a setuptools distribution setup.py. It is usually a name which refers to a function somewhere in a package which is held by the distribution. event An object broadcast to zero or more subscriber callables during normal Pyramid system operations during the lifetime of an application. Application code can subscribe to these events by using the subscriber functionality described in Using Events. exception response A response that is generated as the result of a raised exception being caught by an exception view. Exception view An exception view is a view callable which may be invoked by Pyramid when an exception is raised during request processing. See Custom Exception Views for more information. nished callback A user-dened callback executed by the router unconditionally at the very end of request processing . See Using Finished Callbacks. Forbidden view An exception view invoked by Pyramid when the developer explicitly raises a pyramid.httpexceptions.HTTPForbidden exception from within view code or root factory code, or when the view conguration and authorization policy found for a request disallows a particular view invocation. Pyramid provides a default implementation of a forbidden view; it can be overridden. See Changing the Forbidden View. 692
Genshi An XML templating language by Christopher Lenz. Gettext The GNU gettext library, used by the Pyramid translation machinery. Google App Engine Google App Engine (aka GAE) is a Python application hosting service offered by Google. Pyramid runs on GAE. Green Unicorn Aka gunicorn, a fast WSGI server that runs on UNIX under Python 2.5+ (although at the time of this writing does not support Python 3). See http://gunicorn.org/ for detailed information. Grok A web framework based on Zope 3. HTTP Exception The set of exception classes dened in pyramid.httpexceptions. These can be used to generate responses with various status codes when raised or returned from a view callable. See also HTTP Exceptions. imperative conguration The conguration mode in which you use Python to call methods on a Congurator in order to add each conguration declaration required by your application. interface A Zope interface object. In Pyramid, an interface may be attached to a resource object or a request object in order to identify that the object is of a type. Interfaces are used internally by Pyramid to perform view lookups and other policy lookups. The ability to make use of an interface is exposed to an application programmers during view conguration via the context argument, the request_type argument and the containment argument. Interfaces are also exposed to application developers when they make use of the event system. Fundamentally, Pyramid programmers can think of an interface as something that they can attach to an object that stamps it with a type unrelated to its underlying Python type. Interfaces can also be used to describe the behavior of an object (its methods and attributes), but unless they choose to, Pyramid programmers do not need to understand or use this feature of interfaces. Internationalization The act of creating software with a user interface that can potentially be displayed in more than one language or cultural context. Often shortened to i18n (because the word internationalization is I, 18 letters, then N). See also: Localization. introspectable An object which implements the attributes and methods described in pyramid.interfaces.IIntrospectable. Introspectables are used by the introspector to display conguration information about a running Pyramid application. An introspectable is associated with a action by virtue of the pyramid.config.Configurator.action() method. introspector An object with the methods described by pyramid.interfaces.IIntrospector that is available in both conguration code (for registration) and at runtime (for querying) that allows a developer to introspect conguration statements and relationships between those statements. 693
63. GLOSSARY Jinja2 A text templating language by Armin Ronacher. jQuery A popular Javascript library. JSON JavaScript Object Notation is a data serialization format. Jython A Python implementation written for the Java Virtual Machine. lineage An ordered sequence of objects based on a location -aware resource. The lineage of any given resource is composed of itself, its parent, its parents parent, and so on. The order of the sequence is resource-rst, then the parent of the resource, then its parents parent, and so on. The parent of a resource in a lineage is available as its __parent__ attribute. Lingua A package by Wichert Akkerman which provides Babel message extractors for Python source les and Chameleon ZPT template les. Locale Name A string like en, en_US, de, or de_AT which uniquely identies a particular locale. Locale Negotiator An object supplying a policy determining which locale name best represents a given request. It is used by the pyramid.i18n.get_locale_name(), and pyramid.i18n.negotiate_locale_name() functions, and indirectly by pyramid.i18n.get_localizer(). The pyramid.i18n.default_locale_negotiator() function is an example of a locale negotiator. Localization The process of displaying the user interface of an internationalized application in a particular language or cultural context. Often shortened to l10 (because the word localization is L, 10 letters, then N). See also: Internationalization. Localizer An instance of the class pyramid.i18n.Localizer which provides translation and pluralization services to an application. It is retrieved via the pyramid.i18n.get_localizer() function. location The path to an object in a resource tree. See Location-Aware Resources for more information about how to make a resource object location-aware. Mako Mako is a template language which renes the familiar ideas of componentized layout and inheritance using Python with Python scoping and calling semantics. matchdict The dictionary attached to the request object as request.matchdict when a URL dispatch route has been matched. Its keys are names as identied within the route pattern; its values are the values matched by each pattern name. Message Catalog A gettext .mo le containing translations. 694
Message Identier A string used as a translation lookup key during localization. The msgid argument to a translation string is a message identier. Message identiers are also present in a message catalog. METAL Macro Expansion for TAL, a part of ZPT which makes it possible to share common look and feel between templates. middleware Middleware is a WSGI concept. It is a WSGI component that acts both as a server and an application. Interesting uses for middleware exist, such as caching, content-transport encoding, and other functions. See WSGI.org or PyPI to nd middleware for your application. mod_wsgi mod_wsgi is an Apache module developed by Graham Dumpleton. It allows WSGI applications (such as applications developed using Pyramid) to be served using the Apache web server. module A Python source le; a le on the lesystem that typically ends with the extension .py or .pyc. Modules often live in a package. multidict An ordered dictionary that can have multiple values for each key. Adds the methods getall, getone, mixed, add and dict_of_lists to the normal dictionary interface. See Multidict and pyramid.interfaces.IMultiDict. Not Found view An exception view invoked by Pyramid when the developer explicitly raises a pyramid.httpexceptions.HTTPNotFound exception from within view code or root factory code, or when the current request doesnt match any view conguration. Pyramid provides a default implementation of a not found view; it can be overridden. See Changing the Not Found View. package A directory on disk which contains an __init__.py le, making it recognizable to Python as a location which can be import -ed. A package exists to contain module les. PasteDeploy PasteDeploy is a library used by Pyramid which makes it possible to congure WSGI components together declaratively within an .ini le. It was developed by Ian Bicking. permission A string or unicode object that represents an action being taken against a context resource. A permission is associated with a view name and a resource type by the developer. Resources are decorated with security declarations (e.g. an ACL), which reference these tokens also. Permissions are used by the active security policy to match the view permission against the resourcess statements about which permissions are granted to which principal in a context in order to answer the question is this user allowed to do this. Examples of permissions: read, or view_blog_entries. physical path The path required by a traversal which resolve a resource starting from the physical root. For example, the physical path of the abc subobject of the physical root object is /abc. Physical paths can also be specied as tuples where the rst element is the empty string (representing the root), and every other element is a Unicode object, e.g. (, abc). Physical paths are also sometimes called traversal paths. 695
63. GLOSSARY physical root The object returned by the application root factory. Unlike the virtual root of a request, it is not impacted by Virtual Hosting: it will always be the actual object returned by the root factory, never a subobject. pipeline The PasteDeploy term for a single conguration of a WSGI server, a WSGI application, with a set of middleware in-between. pkg_resources A module which ships with setuptools and distribute that provides an API for addressing asset les within a Python package. Asset les are static les, template les, etc; basically anything non-Python-source that lives in a Python package can be considered a asset le. See also PkgResources predicate A test which returns True or False. Two different types of predicates exist in Pyramid: a view predicate and a route predicate. View predicates are attached to view conguration and route predicates are attached to route conguration. predicate factory A callable which is used by a third party during the registration of a route, view, or subscriber predicates to extend the conguration system. See Adding A Third Party View, Route, or Subscriber Predicate for more information. pregenerator A pregenerator is a function associated by a developer with a route. It is called by route_url() in order to adjust the set of arguments passed to it by the user for special purposes. It will inuence the URL returned by route_url(). See pyramid.interfaces.IRoutePregenerator for more information. principal A principal is a string or unicode object representing a userid or a group id. It is provided by an authentication policy. For example, if a user had the user id bob, and Bob was part of two groups named group foo and group bar, the request might have information attached to it that would indicate that Bob was represented by three principals: bob, group foo and group bar. project (Setuptools/distutils terminology). A directory on disk which contains a setup.py le and one or more Python packages. The setup.py le contains code that allows the package(s) to be installed, distributed, and tested. Pylons A lightweight Python web framework and a predecessor of Pyramid. PyPI The Python Package Index, a collection of software available for Python. PyPy PyPy is an alternative implementation of the Python language:http://pypy.org/ Pyramid Cookbook An additional documentation resource for Pyramid which presents topical, practical usages of Pyramid available via http://docs.pylonsproject.org/ .
696
pyramid_debugtoolbar A Pyramid add on which displays a helpful debug toolbar on top of HTML pages rendered by your application, displaying request, routing, and database information. pyramid_debugtoolbar is congured into the development.ini of all applications which use a Pyramid scaffold. For more information, see http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/dev/ . pyramid_exclog A package which logs Pyramid application exception (error) information to a standard Python logger. This add-on is most useful when used in production applications, because the logger can be congured to log to a le, to UNIX syslog, to the Windows Event Log, or even to email. See its documentation. pyramid_handlers An add-on package which allows Pyramid users to create classes that are analogues of Pylons 1 controllers. See http://docs.pylonsproject.org/projects/pyramid_handlers/dev/ . pyramid_jinja2 Jinja2 templating system bindings for Pyramid, documented at http://docs.pylonsproject.org/projects/pyramid_jinja2/dev/ . This package also includes a scaffold named pyramid_jinja2_starter, which creates an application package based on the Jinja2 templating system. pyramid_zcml An add-on package to Pyramid which allows applications to be congured via ZCML. It is available on PyPI . If you use pyramid_zcml, you can use ZCML as an alternative to imperative conguration or conguration decoration. Python The programming language in which Pyramid is written. renderer A serializer that can be referred to via view conguration which converts a non-Response return values from a view into a string (and ultimately a response). Using a renderer can make writing views that require templating or other serialization less tedious. See Writing View Callables Which Use a Renderer for more information. renderer factory A factory which creates a renderer. See Adding and Changing Renderers for more information. renderer globals Values injected as names into a renderer based on application policy. See Adding Renderer Globals (Deprecated) for more information. Repoze Repoze is essentially a brand of software developed by Agendaless Consulting and a set of contributors. The term has no special intrinsic meaning. The projects website has more information. The software developed under the brand is available in a Subversion repository. Pyramid was originally known as repoze.bfg. repoze.catalog An indexing and search facility (elded and full-text) based on zope.index. See the documentation for more information. 697
63. GLOSSARY repoze.lemonade Zope2 CMF-like data structures and helper facilities for CA-and-ZODB-based applications useful within Pyramid applications. repoze.who Authentication middleware for WSGI applications. It can be used by Pyramid to provide authentication information. repoze.workow Barebones workow for Python apps . It can be used by Pyramid to form a workow system. request An object that represents an HTTP request, usually an instance of the pyramid.request.Request class. See Request and Response Objects (narrative) and pyramid.request (API documentation) for information about request objects. request factory An object which, provided a WSGI environment as a single positional argument, returns a Pyramid-compatible request. request type An attribute of a request that allows for specialization of view invocation based on arbitrary categorization. The every request object that Pyramid generates and manipulates has one or more interface objects attached to it. The default interface attached to a request object is pyramid.interfaces.IRequest. resource An object representing a node in the resource tree of an application. If traversal is used, a resource is an element in the resource tree traversed by the system. When traversal is used, a resource becomes the context of a view. If url dispatch is used, a single resource is generated for each request and is used as the context resource of a view. Resource Location The act of locating a context resource given a request. Traversal and URL dispatch are the resource location subsystems used by Pyramid. resource tree A nested set of dictionary-like objects, each of which is a resource. The act of traversal uses the resource tree to nd a context resource. response An object returned by a view callable that represents response data returned to the requesting user agent. It must implements the pyramid.interfaces.IResponse interface. A response object is typically an instance of the pyramid.response.Response class or a subclass such as pyramid.httpexceptions.HTTPFound. See Request and Response Objects for information about response objects. response adapter A callable which accepts an arbitrary object and converts it to a pyramid.response.Response object. See Changing How Pyramid Treats View Responses for more information. response callback A user-dened callback executed by the router at a point after a response object is successfully created. See Using Response Callbacks. 698
reStructuredText A plain text format that is the defacto standard for descriptive text shipped in distribution les, and Python docstrings. This documentation is authored in ReStructuredText format. root The object at which traversal begins when Pyramid searches for a context resource (for URL Dispatch, the root is always the context resource unless the traverse= argument is used in route conguration). root factory The root factory of a Pyramid application is called on every request sent to the application. The root factory returns the traversal root of an application. It is conventionally named get_root. An application may supply a root factory to Pyramid during the construction of a Congurator. If a root factory is not supplied, the application uses a default root object. Use of the default root object is useful in application which use URL dispatch for all URL-to-view code mappings. route A single pattern matched by the url dispatch subsystem, which generally resolves to a root factory (and then ultimately a view). See also url dispatch. route conguration Route conguration is the act of associating request parameters with a particular route using pattern matching and route predicate statements. See URL Dispatch for more information about route conguration. route predicate An argument to a route conguration which implies a value that evaluates to True or False for a given request. All predicates attached to a route conguration must evaluate to True for the associated route to match the current request. If a route does not match the current request, the next route (in denition order) is attempted. router The WSGI application created when you start a Pyramid application. The router intercepts requests, invokes traversal and/or URL dispatch, calls view functions, and returns responses to the WSGI server on behalf of your Pyramid application. Routes A system by Ben Bangert which parses URLs and compares them against a number of user dened mappings. The URL pattern matching syntax in Pyramid is inspired by the Routes syntax (which was inspired by Ruby On Rails pattern syntax). routes mapper An object which compares path information from a request to an ordered set of route patterns. See URL Dispatch. scaffold A project template that generates some of the major parts of a Pyramid application and helps users to quickly get started writing larger applications. Scaffolds are usually used via the pcreate command. scan The term used by Pyramid to dene the process of importing and examining all code in a Python package or module for conguration decoration. 699
63. GLOSSARY session A namespace that is valid for some period of continual activity that can be used to represent a users interaction with a web application. session factory A callable, which, when called with a single argument named request (a request object), returns a session object. See Using The Default Session Factory, Using Alternate Session Factories and pyramid.config.Configurator.set_session_factory() for more information. setuptools Setuptools builds on Pythons distutils to provide easier building, distribution, and installation of libraries and applications. As of this writing, setuptools runs under Python 2, but not under Python 3. You can use distribute under Python 3 instead. SQLAlchemy SQLAlchemy is an object relational mapper used in tutorials within this documentation. subpath A list of element left over after the router has performed a successful traversal to a view. The subpath is a sequence of strings, e.g. [left, over, names]. Within Pyramid applications that use URL dispatch rather than traversal, you can use *subpath in the route pattern to inuence the subpath. See Using *subpath in a Route Pattern for more information. subscriber A callable which receives an event. A callable becomes a subscriber via imperative conguration or via conguration decoration. See Using Events for more information. template A le with replaceable parts that is capable of representing some text, XML, or HTML when rendered. thread local A thread-local variable is one which is essentially a global variable in terms of how it is accessed and treated, however, each thread used by the application may have a different value for this same global variable. Pyramid uses a small number of thread local variables, as described in Thread Locals. See also the threading.local documentation for more information. Translation Directory A translation directory is a gettext translation directory. It contains language folders, which themselves contain LC_MESSAGES folders, which contain .mo les. Each .mo le represents a set of translations for a language in a translation domain. The name of the .mo le (minus the .mo extension) is the translation domain name. Translation Domain A string representing the context in which a translation was made. For example the word java might be translated differently if the translation domain is programminglanguages than would be if the translation domain was coffee. A translation domain is represnted by a collection of .mo les within one or more translation directory directories. Translation String An instance of pyramid.i18n.TranslationString, which is a class that behaves like a Unicode string, but has several extra attributes such as domain, msgid, and mapping for use during translation. Translation strings are usually created by hand within software, but are sometimes created on the behalf of the system for automatic template translation. For more information, see Internationalization and Localization. 700
Translator A callable which receives a translation string and returns a translated Unicode object for the purposes of internationalization. A localizer supplies a translator to a Pyramid application accessible via its translate method. traversal The act of descending up a tree of resource objects from a root resource in order to nd a context resource. The Pyramid router performs traversal of resource objects when a root factory is specied. See the Traversal chapter for more information. Traversal can be performed instead of URL dispatch or can be combined with URL dispatch. See Combining Traversal and URL Dispatch for more information about combining traversal and URL dispatch (advanced). tween A bit of code that sits between the Pyramid routers main request handling function and the upstream WSGI component that uses Pyramid as its app. The word tween is a contraction of between. A tween may be used by Pyramid framework extensions, to provide, for example, Pyramid-specic view timing support, bookkeeping code that examines exceptions before they are returned to the upstream WSGI application, or a variety of other features. Tweens behave a bit like WSGI middleware but they have the benet of running in a context in which they have access to the Pyramid application registry as well as the Pyramid rendering machinery. See Registering Tweens. URL dispatch An alternative to traversal as a mechanism for locating a context resource for a view. When you use a route in your Pyramid application via a route conguration, you are using URL dispatch. See the URL Dispatch for more information. Venusian Venusian is a library which allows framework authors to defer decorator actions. Instead of taking actions when a function (or class) decorator is executed at import time, the action usually taken by the decorator is deferred until a separate scan phase. Pyramid relies on Venusian to provide a basis for its scan feature. view Common vernacular for a view callable. view callable A view callable is a callable Python object which is associated with a view conguration; it returns a response object . A view callable accepts a single argument: request, which will be an instance of a request object. An alternate calling convention allows a view to be dened as a callable which accepts a pair of arguments: context and request: this calling convention is useful for traversal-based applications in which a context is always very important. A view callable is the primary mechanism by which a developer writes user interface code within Pyramid. See Views for more information about Pyramid view callables. view conguration View conguration is the act of associating a view callable with conguration information. This conguration information helps map a given request to a particular view callable and it can inuence the response of a view callable. Pyramid views can be congured via imperative conguration, or by a special @view_config decorator coupled with a scan. See View Conguration for more information about view conguration. 701
63. GLOSSARY View handler A view handler ties together pyramid.config.Configurator.add_route() and pyramid.config.Configurator.add_view() to make it more convenient to register a collection of views as a single class when using url dispatch. View handlers ship as part of the pyramid_handlers add-on package. View Lookup The act of nding and invoking the best view callable given a request and a context resource. view mapper A view mapper is a class which implements the pyramid.interfaces.IViewMapperFactory interface, which performs view argument and return value mapping. This is a plug point for extension builders, not normally used by civilians. view name The URL name of a view, e.g index.html. If a view is congured without a name, its name is considered to be the empty string (which implies the default view). view predicate An argument to a view conguration which evaluates to True or False for a given request. All predicates attached to a view conguration must evaluate to true for the associated view to be considered as a possible callable for a given request. virtual root A resource object representing the virtual root of a request; this is typically the physical root object unless Virtual Hosting is in use. virtualenv An isolated Python environment. Allows you to control which packages are used on a particular project by cloning your main Python. virtualenv was created by Ian Bicking. Waitress A WSGI server that runs on UNIX and Windows under Python 2.6+ and Python 3.2+. Projects generated via Pyramid scaffolding use Waitress as a WGSI server. See http://docs.pylonsproject.org/projects/waitress/en/latest/ for detailed information. WebOb WebOb is a WSGI request/response library created by Ian Bicking. WebTest WebTest is a package which can help you write functional tests for your WSGI application. WSGI Web Server Gateway Interface. This is a Python standard for connecting web applications to web servers, similar to the concept of Java Servlets. Pyramid requires that your application be served as a WSGI application. ZCML Zope Conguration Markup Language, an XML dialect used by Zope and pyramid_zcml for conguration tasks. ZEO Zope Enterprise Objects allows multiple simultaneous processes to access a single ZODB database. ZODB Zope Object Database, a persistent Python object store. Zope The Z Object Publishing Framework, a full-featured Python web framework. Zope Component Architecture The Zope Component Architecture (aka ZCA) is a system which allows for application pluggability and complex dispatching based on objects which implement an interface. Pyramid uses the ZCA under the hood to perform view dispatching and other application conguration tasks. ZPT The Zope Page Template templating language. 702