summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/Makefile9
-rw-r--r--docs/_static/directory_structure_generic.pngbin0 -> 11845 bytes
-rw-r--r--docs/_static/directory_structure_initial.pngbin0 -> 7752 bytes
-rw-r--r--docs/_static/directory_structure_pyramid.pngbin0 -> 16746 bytes
m---------docs/_themes0
-rw-r--r--docs/api/config.rst5
-rw-r--r--docs/api/registry.rst9
-rw-r--r--docs/api/request.rst46
-rw-r--r--docs/api/view.rst6
-rw-r--r--docs/authorintro.rst4
-rw-r--r--docs/conf.py38
-rw-r--r--docs/copyright.rst2
-rw-r--r--docs/designdefense.rst79
-rw-r--r--docs/foreword.rst2
-rw-r--r--docs/glossary.rst47
-rw-r--r--docs/index.rst116
-rw-r--r--docs/latexindex.rst47
-rw-r--r--docs/narr/MyProject/myproject/__init__.py1
-rw-r--r--docs/narr/MyProject/myproject/templates/mytemplate.pt4
-rw-r--r--docs/narr/MyProject/setup.py1
-rw-r--r--docs/narr/advconfig.rst1
-rw-r--r--docs/narr/assets.rst20
-rw-r--r--docs/narr/commandline.rst30
-rw-r--r--docs/narr/environment.rst2
-rw-r--r--docs/narr/events.rst93
-rw-r--r--docs/narr/extconfig.rst4
-rw-r--r--docs/narr/hooks.rst114
-rw-r--r--docs/narr/hybrid.rst102
-rw-r--r--docs/narr/i18n.rst129
-rw-r--r--docs/narr/install.rst371
-rw-r--r--docs/narr/introduction.rst27
-rw-r--r--docs/narr/introspector.rst12
-rw-r--r--docs/narr/project.rst133
-rw-r--r--docs/narr/renderers.rst315
-rw-r--r--docs/narr/resources.rst2
-rw-r--r--docs/narr/sessions.rst113
-rw-r--r--docs/narr/subrequest.rst6
-rw-r--r--docs/narr/templates.rst438
-rw-r--r--docs/narr/testing.rst16
-rw-r--r--docs/narr/traversal.rst32
-rw-r--r--docs/narr/upgrading.rst2
-rw-r--r--docs/narr/urldispatch.rst44
-rw-r--r--docs/narr/viewconfig.rst54
-rw-r--r--docs/narr/webob.rst2
-rw-r--r--docs/quick_tour.rst890
-rw-r--r--docs/quick_tour/awesome/CHANGES.txt4
-rw-r--r--docs/quick_tour/awesome/MANIFEST.in2
-rw-r--r--docs/quick_tour/awesome/README.txt4
-rw-r--r--docs/quick_tour/awesome/awesome/__init__.py23
-rw-r--r--docs/quick_tour/awesome/awesome/locale/awesome.pot21
-rw-r--r--docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.mobin0 -> 460 bytes
-rw-r--r--docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.po21
-rw-r--r--docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.mobin0 -> 461 bytes
-rw-r--r--docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.po21
-rw-r--r--docs/quick_tour/awesome/awesome/models.py8
-rw-r--r--docs/quick_tour/awesome/awesome/static/favicon.icobin0 -> 1406 bytes
-rw-r--r--docs/quick_tour/awesome/awesome/static/logo.pngbin0 -> 6641 bytes
-rw-r--r--docs/quick_tour/awesome/awesome/static/pylons.css73
-rw-r--r--docs/quick_tour/awesome/awesome/templates/mytemplate.jinja287
-rw-r--r--docs/quick_tour/awesome/awesome/tests.py21
-rw-r--r--docs/quick_tour/awesome/awesome/views.py6
-rw-r--r--docs/quick_tour/awesome/development.ini49
-rw-r--r--docs/quick_tour/awesome/message-extraction.ini3
-rw-r--r--docs/quick_tour/awesome/setup.cfg28
-rw-r--r--docs/quick_tour/awesome/setup.py36
-rw-r--r--docs/quick_tour/hello_world/app.py16
-rw-r--r--docs/quick_tour/jinja2/app.py11
-rw-r--r--docs/quick_tour/jinja2/hello_world.jinja29
-rw-r--r--docs/quick_tour/jinja2/views.py8
-rw-r--r--docs/quick_tour/json/app.py15
-rw-r--r--docs/quick_tour/json/hello_world.jinja210
-rw-r--r--docs/quick_tour/json/hello_world.pt17
-rw-r--r--docs/quick_tour/json/views.py13
-rw-r--r--docs/quick_tour/package/CHANGES.txt4
-rw-r--r--docs/quick_tour/package/MANIFEST.in2
-rw-r--r--docs/quick_tour/package/README.txt4
-rw-r--r--docs/quick_tour/package/development.ini54
-rw-r--r--docs/quick_tour/package/hello_world/__init__.py34
-rw-r--r--docs/quick_tour/package/hello_world/init.py34
-rw-r--r--docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.mobin0 -> 460 bytes
-rw-r--r--docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.po21
-rw-r--r--docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.mobin0 -> 461 bytes
-rw-r--r--docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.po21
-rw-r--r--docs/quick_tour/package/hello_world/locale/hello_world.pot21
-rw-r--r--docs/quick_tour/package/hello_world/models.py8
-rw-r--r--docs/quick_tour/package/hello_world/static/favicon.icobin0 -> 1406 bytes
-rw-r--r--docs/quick_tour/package/hello_world/static/logo.pngbin0 -> 6641 bytes
-rw-r--r--docs/quick_tour/package/hello_world/static/pylons.css73
-rw-r--r--docs/quick_tour/package/hello_world/templates/mytemplate.jinja290
-rw-r--r--docs/quick_tour/package/hello_world/tests.py20
-rw-r--r--docs/quick_tour/package/hello_world/views.py22
-rw-r--r--docs/quick_tour/package/message-extraction.ini3
-rw-r--r--docs/quick_tour/package/setup.cfg28
-rw-r--r--docs/quick_tour/package/setup.py41
-rw-r--r--docs/quick_tour/requests/app.py24
-rw-r--r--docs/quick_tour/routing/app.py12
-rw-r--r--docs/quick_tour/routing/views.py10
-rw-r--r--docs/quick_tour/sqla_demo/CHANGES.txt4
-rw-r--r--docs/quick_tour/sqla_demo/MANIFEST.in2
-rw-r--r--docs/quick_tour/sqla_demo/README.txt14
-rw-r--r--docs/quick_tour/sqla_demo/development.ini71
-rw-r--r--docs/quick_tour/sqla_demo/production.ini62
-rw-r--r--docs/quick_tour/sqla_demo/setup.cfg27
-rw-r--r--docs/quick_tour/sqla_demo/setup.py44
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo.sqlitebin0 -> 3072 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/__init__.py20
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/models.py29
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/scripts/__init__.py1
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/scripts/initializedb.py37
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/favicon.icobin0 -> 1406 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/ie6.css8
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/pylons.css372
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/templates/mytemplate.pt76
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/tests.py33
-rw-r--r--docs/quick_tour/sqla_demo/sqla_demo/views.py37
-rw-r--r--docs/quick_tour/static_assets/app.py14
-rw-r--r--docs/quick_tour/static_assets/hello_world.jinja210
-rw-r--r--docs/quick_tour/static_assets/hello_world.pt16
-rw-r--r--docs/quick_tour/static_assets/static/app.css4
-rw-r--r--docs/quick_tour/static_assets/views.py6
-rw-r--r--docs/quick_tour/templating/app.py10
-rw-r--r--docs/quick_tour/templating/hello_world.pt9
-rw-r--r--docs/quick_tour/templating/views.py8
-rw-r--r--docs/quick_tour/view_classes/app.py13
-rw-r--r--docs/quick_tour/view_classes/delete.jinja29
-rw-r--r--docs/quick_tour/view_classes/edit.jinja29
-rw-r--r--docs/quick_tour/view_classes/hello.jinja217
-rw-r--r--docs/quick_tour/view_classes/views.py32
-rw-r--r--docs/quick_tour/views/app.py13
-rw-r--r--docs/quick_tour/views/views.py29
-rw-r--r--docs/quick_tutorial/authentication.rst134
-rw-r--r--docs/quick_tutorial/authentication/development.ini42
-rw-r--r--docs/quick_tutorial/authentication/setup.py14
-rw-r--r--docs/quick_tutorial/authentication/tutorial/__init__.py25
-rw-r--r--docs/quick_tutorial/authentication/tutorial/home.pt18
-rw-r--r--docs/quick_tutorial/authentication/tutorial/login.pt25
-rw-r--r--docs/quick_tutorial/authentication/tutorial/security.py8
-rw-r--r--docs/quick_tutorial/authentication/tutorial/views.py64
-rw-r--r--docs/quick_tutorial/authorization.rst112
-rw-r--r--docs/quick_tutorial/authorization/development.ini42
-rw-r--r--docs/quick_tutorial/authorization/setup.py14
-rw-r--r--docs/quick_tutorial/authorization/tutorial/__init__.py26
-rw-r--r--docs/quick_tutorial/authorization/tutorial/home.pt18
-rw-r--r--docs/quick_tutorial/authorization/tutorial/login.pt25
-rw-r--r--docs/quick_tutorial/authorization/tutorial/resources.py9
-rw-r--r--docs/quick_tutorial/authorization/tutorial/security.py8
-rw-r--r--docs/quick_tutorial/authorization/tutorial/views.py66
-rw-r--r--docs/quick_tutorial/conf.py281
-rw-r--r--docs/quick_tutorial/databases.rst195
-rw-r--r--docs/quick_tutorial/databases/development.ini49
-rw-r--r--docs/quick_tutorial/databases/setup.py20
-rw-r--r--docs/quick_tutorial/databases/sqltutorial.sqlitebin0 -> 12288 bytes
-rw-r--r--docs/quick_tutorial/databases/tutorial/__init__.py21
-rw-r--r--docs/quick_tutorial/databases/tutorial/initialize_db.py37
-rw-r--r--docs/quick_tutorial/databases/tutorial/models.py35
-rw-r--r--docs/quick_tutorial/databases/tutorial/tests.py58
-rw-r--r--docs/quick_tutorial/databases/tutorial/views.py96
-rw-r--r--docs/quick_tutorial/databases/tutorial/wiki_view.pt19
-rw-r--r--docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt22
-rw-r--r--docs/quick_tutorial/databases/tutorial/wikipage_view.pt17
-rw-r--r--docs/quick_tutorial/debugtoolbar.rst89
-rw-r--r--docs/quick_tutorial/debugtoolbar/development.ini40
-rw-r--r--docs/quick_tutorial/debugtoolbar/setup.py13
-rw-r--r--docs/quick_tutorial/debugtoolbar/tutorial/__init__.py13
-rw-r--r--docs/quick_tutorial/forms.rst148
-rw-r--r--docs/quick_tutorial/forms/development.ini41
-rw-r--r--docs/quick_tutorial/forms/setup.py15
-rw-r--r--docs/quick_tutorial/forms/tutorial/__init__.py13
-rw-r--r--docs/quick_tutorial/forms/tutorial/tests.py36
-rw-r--r--docs/quick_tutorial/forms/tutorial/views.py96
-rw-r--r--docs/quick_tutorial/forms/tutorial/wiki_view.pt19
-rw-r--r--docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt22
-rw-r--r--docs/quick_tutorial/forms/tutorial/wikipage_view.pt17
-rw-r--r--docs/quick_tutorial/functional_testing.rst70
-rw-r--r--docs/quick_tutorial/functional_testing/development.ini40
-rw-r--r--docs/quick_tutorial/functional_testing/setup.py13
-rw-r--r--docs/quick_tutorial/functional_testing/tutorial/__init__.py13
-rw-r--r--docs/quick_tutorial/functional_testing/tutorial/tests.py31
-rw-r--r--docs/quick_tutorial/hello_world.rst114
-rw-r--r--docs/quick_tutorial/hello_world/app.py17
-rw-r--r--docs/quick_tutorial/index.rst50
-rw-r--r--docs/quick_tutorial/ini.rst146
-rw-r--r--docs/quick_tutorial/ini/development.ini38
-rw-r--r--docs/quick_tutorial/ini/setup.py13
-rw-r--r--docs/quick_tutorial/ini/tutorial/__init__.py13
-rw-r--r--docs/quick_tutorial/jinja2.rst98
-rw-r--r--docs/quick_tutorial/jinja2/development.ini42
-rw-r--r--docs/quick_tutorial/jinja2/setup.py13
-rw-r--r--docs/quick_tutorial/jinja2/tutorial/__init__.py9
-rw-r--r--docs/quick_tutorial/jinja2/tutorial/home.jinja29
-rw-r--r--docs/quick_tutorial/jinja2/tutorial/tests.py50
-rw-r--r--docs/quick_tutorial/jinja2/tutorial/views.py18
-rw-r--r--docs/quick_tutorial/json.rst103
-rw-r--r--docs/quick_tutorial/json/development.ini41
-rw-r--r--docs/quick_tutorial/json/setup.py14
-rw-r--r--docs/quick_tutorial/json/tutorial/__init__.py11
-rw-r--r--docs/quick_tutorial/json/tutorial/home.pt9
-rw-r--r--docs/quick_tutorial/json/tutorial/tests.py50
-rw-r--r--docs/quick_tutorial/json/tutorial/views.py19
-rw-r--r--docs/quick_tutorial/logging.rst79
-rw-r--r--docs/quick_tutorial/logging/development.ini41
-rw-r--r--docs/quick_tutorial/logging/setup.py14
-rw-r--r--docs/quick_tutorial/logging/tutorial/__init__.py10
-rw-r--r--docs/quick_tutorial/logging/tutorial/home.pt9
-rw-r--r--docs/quick_tutorial/logging/tutorial/tests.py44
-rw-r--r--docs/quick_tutorial/logging/tutorial/views.py23
-rw-r--r--docs/quick_tutorial/more_view_classes.rst182
-rw-r--r--docs/quick_tutorial/more_view_classes/development.ini41
-rw-r--r--docs/quick_tutorial/more_view_classes/setup.py14
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/__init__.py10
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/delete.pt9
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/edit.pt10
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/hello.pt16
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/home.pt12
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/tests.py31
-rw-r--r--docs/quick_tutorial/more_view_classes/tutorial/views.py39
-rw-r--r--docs/quick_tutorial/package.rst112
-rw-r--r--docs/quick_tutorial/package/setup.py9
-rw-r--r--docs/quick_tutorial/package/tutorial/__init__.py1
-rw-r--r--docs/quick_tutorial/package/tutorial/app.py17
-rw-r--r--docs/quick_tutorial/request_response.rst103
-rw-r--r--docs/quick_tutorial/request_response/development.ini41
-rw-r--r--docs/quick_tutorial/request_response/setup.py13
-rw-r--r--docs/quick_tutorial/request_response/tutorial/__init__.py9
-rw-r--r--docs/quick_tutorial/request_response/tutorial/tests.py54
-rw-r--r--docs/quick_tutorial/request_response/tutorial/views.py22
-rw-r--r--docs/quick_tutorial/requirements.rst250
-rw-r--r--docs/quick_tutorial/routing.rst121
-rw-r--r--docs/quick_tutorial/routing/development.ini41
-rw-r--r--docs/quick_tutorial/routing/setup.py14
-rw-r--r--docs/quick_tutorial/routing/tutorial/__init__.py9
-rw-r--r--docs/quick_tutorial/routing/tutorial/home.pt10
-rw-r--r--docs/quick_tutorial/routing/tutorial/tests.py36
-rw-r--r--docs/quick_tutorial/routing/tutorial/views.py20
-rw-r--r--docs/quick_tutorial/scaffolds.rst86
-rw-r--r--docs/quick_tutorial/scaffolds/CHANGES.txt4
-rw-r--r--docs/quick_tutorial/scaffolds/MANIFEST.in2
-rw-r--r--docs/quick_tutorial/scaffolds/README.txt1
-rw-r--r--docs/quick_tutorial/scaffolds/development.ini60
-rw-r--r--docs/quick_tutorial/scaffolds/production.ini54
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/__init__.py12
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/favicon.icobin0 -> 1406 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.pngbin0 -> 333 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.pngbin0 -> 203 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css8
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.pngbin0 -> 2797 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css372
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.pngbin0 -> 7044 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.pngbin0 -> 33055 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gifbin0 -> 49 bytes
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt73
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/tests.py17
-rw-r--r--docs/quick_tutorial/scaffolds/scaffolds/views.py6
-rw-r--r--docs/quick_tutorial/scaffolds/setup.cfg27
-rw-r--r--docs/quick_tutorial/scaffolds/setup.py42
-rw-r--r--docs/quick_tutorial/sessions.rst100
-rw-r--r--docs/quick_tutorial/sessions/development.ini41
-rw-r--r--docs/quick_tutorial/sessions/setup.py14
-rw-r--r--docs/quick_tutorial/sessions/tutorial/__init__.py14
-rw-r--r--docs/quick_tutorial/sessions/tutorial/home.pt10
-rw-r--r--docs/quick_tutorial/sessions/tutorial/tests.py44
-rw-r--r--docs/quick_tutorial/sessions/tutorial/views.py29
-rw-r--r--docs/quick_tutorial/static_assets.rst91
-rw-r--r--docs/quick_tutorial/static_assets/development.ini41
-rw-r--r--docs/quick_tutorial/static_assets/setup.py14
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/__init__.py11
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/home.pt11
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/static/app.css4
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/tests.py44
-rw-r--r--docs/quick_tutorial/static_assets/tutorial/views.py18
-rw-r--r--docs/quick_tutorial/templating.rst123
-rw-r--r--docs/quick_tutorial/templating/development.ini41
-rw-r--r--docs/quick_tutorial/templating/setup.py14
-rw-r--r--docs/quick_tutorial/templating/tutorial/__init__.py10
-rw-r--r--docs/quick_tutorial/templating/tutorial/home.pt9
-rw-r--r--docs/quick_tutorial/templating/tutorial/tests.py44
-rw-r--r--docs/quick_tutorial/templating/tutorial/views.py13
-rw-r--r--docs/quick_tutorial/tutorial_approach.rst45
-rw-r--r--docs/quick_tutorial/unit_testing.rst119
-rw-r--r--docs/quick_tutorial/unit_testing/development.ini40
-rw-r--r--docs/quick_tutorial/unit_testing/setup.py13
-rw-r--r--docs/quick_tutorial/unit_testing/tutorial/__init__.py13
-rw-r--r--docs/quick_tutorial/unit_testing/tutorial/tests.py18
-rw-r--r--docs/quick_tutorial/view_classes.rst98
-rw-r--r--docs/quick_tutorial/view_classes/development.ini41
-rw-r--r--docs/quick_tutorial/view_classes/setup.py14
-rw-r--r--docs/quick_tutorial/view_classes/tutorial/__init__.py10
-rw-r--r--docs/quick_tutorial/view_classes/tutorial/home.pt9
-rw-r--r--docs/quick_tutorial/view_classes/tutorial/tests.py44
-rw-r--r--docs/quick_tutorial/view_classes/tutorial/views.py17
-rw-r--r--docs/quick_tutorial/views.rst122
-rw-r--r--docs/quick_tutorial/views/development.ini40
-rw-r--r--docs/quick_tutorial/views/setup.py13
-rw-r--r--docs/quick_tutorial/views/tutorial/__init__.py9
-rw-r--r--docs/quick_tutorial/views/tutorial/tests.py44
-rw-r--r--docs/quick_tutorial/views/tutorial/views.py14
-rw-r--r--docs/tutorials/bfg/index.rst204
-rw-r--r--docs/tutorials/modwsgi/index.rst2
-rw-r--r--docs/tutorials/wiki/basiclayout.rst9
-rw-r--r--docs/tutorials/wiki/definingviews.rst4
-rw-r--r--docs/tutorials/wiki/src/authorization/setup.py1
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki/src/basiclayout/setup.py1
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki/src/models/setup.py1
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki/src/models/tutorial/tests.py2
-rw-r--r--docs/tutorials/wiki/src/tests/setup.py1
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki/src/views/setup.py1
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki/tests.rst4
-rw-r--r--docs/tutorials/wiki2/authorization.rst8
-rw-r--r--docs/tutorials/wiki2/basiclayout.rst30
-rw-r--r--docs/tutorials/wiki2/definingmodels.rst2
-rw-r--r--docs/tutorials/wiki2/definingviews.rst8
-rw-r--r--docs/tutorials/wiki2/installation.rst6
-rw-r--r--docs/tutorials/wiki2/src/authorization/setup.py5
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/models.py3
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py2
-rw-r--r--docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/setup.py5
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/models.py4
-rw-r--r--docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki2/src/models/setup.py5
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/models.py4
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py2
-rw-r--r--docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki2/src/tests/setup.py5
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/models.py3
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py2
-rw-r--r--docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki2/src/views/setup.py5
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/__init__.py1
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/models.py4
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py2
-rw-r--r--docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt4
-rw-r--r--docs/tutorials/wiki2/tests.rst4
-rw-r--r--docs/whatsnew-1.0.rst6
-rw-r--r--docs/whatsnew-1.1.rst13
-rw-r--r--docs/whatsnew-1.5.rst433
356 files changed, 11404 insertions, 1672 deletions
diff --git a/docs/Makefile b/docs/Makefile
index c98fdc65e..12dc88bf8 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -63,8 +63,13 @@ latex:
cp _static/latex-note.png _build/latex
@echo
@echo "Build finished; the LaTeX files are in _build/latex."
- @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
- "run these through (pdf)latex."
+ @echo "Run \`make latexpdf' to build a PDF file from them."
+
+latexpdf:
+ $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) _build/latex
+ @echo "Running LaTeX files through pdflatex..."
+ $(MAKE) -C _build/latex all-pdf
+ @echo "pdflatex finished; the PDF file is in _build/latex."
changes:
mkdir -p _build/changes _build/doctrees
diff --git a/docs/_static/directory_structure_generic.png b/docs/_static/directory_structure_generic.png
new file mode 100644
index 000000000..c6d1a5b03
--- /dev/null
+++ b/docs/_static/directory_structure_generic.png
Binary files differ
diff --git a/docs/_static/directory_structure_initial.png b/docs/_static/directory_structure_initial.png
new file mode 100644
index 000000000..000f1bb27
--- /dev/null
+++ b/docs/_static/directory_structure_initial.png
Binary files differ
diff --git a/docs/_static/directory_structure_pyramid.png b/docs/_static/directory_structure_pyramid.png
new file mode 100644
index 000000000..74edd6533
--- /dev/null
+++ b/docs/_static/directory_structure_pyramid.png
Binary files differ
diff --git a/docs/_themes b/docs/_themes
-Subproject f59f7bfce5259f50fbb67b9040c03ecb080130b
+Subproject 26732645619b372764097e5e8086f89871d90c0
diff --git a/docs/api/config.rst b/docs/api/config.rst
index 39d504348..48dd2f0b9 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -52,10 +52,6 @@
.. automethod:: override_asset(to_override, override_with)
- :methodcategory:`Setting Renderer Globals`
-
- .. automethod:: set_renderer_globals_factory(factory)
-
:methodcategory:`Getting and Adding Settings`
.. automethod:: add_settings
@@ -135,3 +131,4 @@
will only exist for the lifetime of the actual applications for which they
are being used.
+.. autoclass:: not_
diff --git a/docs/api/registry.rst b/docs/api/registry.rst
index db348495c..7736cf075 100644
--- a/docs/api/registry.rst
+++ b/docs/api/registry.rst
@@ -29,6 +29,15 @@
This attribute is often accessed as ``request.registry.introspector`` in
a typical Pyramid application.
+ .. method:: notify(*events)
+
+ Fire one or more events. All event subscribers to the event(s)
+ will be notified. The subscribers will be called synchronously.
+ This method is often accessed as ``request.registry.notify``
+ in Pyramid applications to fire custom events. See
+ :ref:`custom_events` for more information.
+
+
.. class:: Introspectable
.. versionadded:: 1.3
diff --git a/docs/api/request.rst b/docs/api/request.rst
index 7b843f86e..72abddb68 100644
--- a/docs/api/request.rst
+++ b/docs/api/request.rst
@@ -156,7 +156,7 @@
.. attribute:: matched_route
If a :term:`route` has matched during this request, this attribute will
- be an obect representing the route matched by the URL pattern
+ be an object 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
:ref:`matched_route`.
@@ -190,22 +190,22 @@
:meth:`~pyramid.config.Configurator.set_request_property`) on the
request it's passed.
- - causes a :class:`~pyramid.event.NewRequest` event to be sent at the
+ - causes a :class:`~pyramid.events.NewRequest` event to be sent at the
beginning of request processing.
- - causes a :class:`~pyramid.event.ContextFound` event to be sent
+ - causes a :class:`~pyramid.events.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 :class:`~pyramid.event.NewResponse` event to be sent when
- the Pyramid application returns a response.
-
- Calls any :term:`response callback` functions defined within the
request's lifetime if a response is obtained from the Pyramid
application.
+ - causes a :class:`~pyramid.events.NewResponse` event to be sent if a
+ response is obtained.
+
- Calls any :term:`finished callback` functions defined within the
request's lifetime.
@@ -235,24 +235,6 @@
.. automethod:: resource_path
- .. attribute:: response_*
-
- In Pyramid 1.0, you could set attributes on a
- :class:`pyramid.request.Request` which influenced the behavor of
- *rendered* responses (views which use a :term:`renderer` and which
- don't directly return a response). These attributes began with
- ``response_``, such as ``response_headerlist``. If you needed to
- influence 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 :ref:`response_prefixed_attrs` for further discussion.
- As of Pyramid 1.1, assignment to ``response_*`` attrs is 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 :attr:`pyramid.request.Request.response`
- object (exposed to view code as ``request.response``) to influence
- rendered response behavior.
-
.. attribute:: json_body
This property will return the JSON-decoded variant of the request
@@ -311,6 +293,20 @@
.. versionadded:: 1.3
+ .. attribute:: localizer
+
+ A :term:`localizer` which will use the current locale name to
+ translate values.
+
+ .. versionadded:: 1.5
+
+ .. attribute:: locale_name
+
+ The locale name of the current request as computed by the
+ :term:`locale negotiator`.
+
+ .. versionadded:: 1.5
+
.. note::
For information about the API of a :term:`multidict` structure (such as
diff --git a/docs/api/view.rst b/docs/api/view.rst
index 21d2bb90d..d8e429552 100644
--- a/docs/api/view.rst
+++ b/docs/api/view.rst
@@ -11,8 +11,6 @@
.. autofunction:: render_view
- .. autofunction:: is_response
-
.. autoclass:: view_config
:members:
@@ -25,8 +23,4 @@
.. autoclass:: forbidden_view_config
:members:
- .. autoclass:: static
- :members:
- :inherited-members:
-
diff --git a/docs/authorintro.rst b/docs/authorintro.rst
index f1a9d1484..ebc6bcff8 100644
--- a/docs/authorintro.rst
+++ b/docs/authorintro.rst
@@ -2,7 +2,7 @@
Author Introduction
=====================
-Welcome to "The :app:`Pyramid` Web Application Framework". In this
+Welcome to "The :app:`Pyramid` Web Framework". In this
introduction, I'll describe the audience for this book, I'll describe
the book content, I'll provide some context regarding the genesis of
:app:`Pyramid`, and I'll thank some important people.
@@ -73,7 +73,7 @@ This book is divided into three major parts:
concepts in terms of the sample. You should read the tutorials if
you want a guided tour of :app:`Pyramid`.
-:ref:`api_reference`
+:ref:`api_documentation`
Comprehensive reference material for every public API exposed by
:app:`Pyramid`. The API documentation is organized
diff --git a/docs/conf.py b/docs/conf.py
index 33bedc9dd..f18cf3ec3 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -19,6 +19,8 @@ import warnings
warnings.simplefilter('ignore', DeprecationWarning)
+import pkg_resources
+
# skip raw nodes
from sphinx.writers.text import TextTranslator
from sphinx.writers.latex import LaTeXTranslator
@@ -55,9 +57,21 @@ extensions = [
# Looks for objects in external projects
intersphinx_mapping = {
+ 'tutorials': ('http://docs.pylonsproject.org/projects/pyramid_tutorials/en/latest/', None),
+ 'jinja2': ('http://docs.pylonsproject.org/projects/pyramid_jinja2/en/latest/', None),
+ 'tm': (
+ 'http://docs.pylonsproject.org/projects/pyramid_tm/en/latest/',
+ None,
+ ),
'zcomponent': ('http://docs.zope.org/zope.component', None),
'webtest': ('http://webtest.pythonpaste.org/en/latest', None),
'webob': ('http://docs.webob.org/en/latest', None),
+ 'colander': (
+ 'http://docs.pylonsproject.org/projects/colander/en/latest',
+ None),
+ 'deform': (
+ 'http://docs.pylonsproject.org/projects/deform/en/latest',
+ None),
'sqla': ('http://docs.sqlalchemy.org/en/latest', None),
'who': ('http://docs.repoze.org/who/latest', None),
'python': ('http://docs.python.org', None),
@@ -70,8 +84,12 @@ intersphinx_mapping = {
'toolbar':
('http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/en/latest',
None),
+ 'zcml':
+ ('http://docs.pylonsproject.org/projects/pyramid_zcml/en/latest',
+ None),
}
+
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
@@ -82,7 +100,7 @@ source_suffix = '.rst'
master_doc = 'index'
# General substitutions.
-project = 'The Pyramid Web Application Development Framework'
+project = 'The Pyramid Web Framework'
thisyear = datetime.datetime.now().year
copyright = '2008-%s, Agendaless Consulting' % thisyear
@@ -90,7 +108,7 @@ copyright = '2008-%s, Agendaless Consulting' % thisyear
# other places throughout the built documents.
#
# The short X.Y version.
-version = '1.4'
+version = pkg_resources.get_distribution('pyramid').version
# The full version, including alpha/beta/rc tags.
release = version
@@ -147,17 +165,21 @@ html_theme_path = ['_themes']
html_theme = 'pyramid'
html_theme_options = dict(
github_url='https://github.com/Pylons/pyramid',
-# in_progress='true',
+ in_progress='true',
)
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
-html_title = 'The Pyramid Web Application Development Framework v%s' % release
+html_title = 'The Pyramid Web Framework v%s' % release
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
html_last_updated_fmt = '%b %d, %Y'
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+html_use_smartypants = False # people use cutnpaste in some places
+
# Output file base name for HTML help builder.
htmlhelp_basename = 'pyramid'
@@ -176,7 +198,7 @@ latex_additional_files = ['_static/latex-note.png', '_static/latex-warning.png']
# (source start file, target name, title, author, document class [howto/manual]).
latex_documents = [
('latexindex', 'pyramid.tex',
- 'The Pyramid Web Application Development Framework',
+ 'The Pyramid Web Framework',
'Chris McDonough', 'manual'),
]
@@ -301,7 +323,7 @@ latex_elements = {
'wrapperclass': 'book',
'date': '',
'releasename': 'Version',
- 'title': r'The Pyramid Web Application \newline Development Framework',
+ 'title': r'The Pyramid Web Framework',
# 'pointsize':'12pt', # uncomment for 12pt version
}
@@ -406,7 +428,7 @@ def resig(app, what, name, obj, options, signature, return_annotation):
# -- Options for Epub output ---------------------------------------------------
# Bibliographic Dublin Core info.
-epub_title = 'The Pyramid Web Application Development Framework, Version %s' \
+epub_title = 'The Pyramid Web Framework, Version %s' \
% release
epub_author = 'Chris McDonough'
epub_publisher = 'Agendaless Consulting'
@@ -424,7 +446,7 @@ epub_scheme = 'ISBN'
epub_identifier = '0615445675'
# A unique identification for the text.
-epub_uid = 'The Pyramid Web Application Development Framework, Version %s' \
+epub_uid = 'The Pyramid Web Framework, Version %s' \
% release
# A list of files that should not be packed into the epub file.
diff --git a/docs/copyright.rst b/docs/copyright.rst
index 8f3a5f5e7..980335827 100644
--- a/docs/copyright.rst
+++ b/docs/copyright.rst
@@ -1,7 +1,7 @@
Copyright, Trademarks, and Attributions
=======================================
-*The Pyramid Web Application Development Framework, Version 1.1*
+*The Pyramid Web Framework, Version 1.1*
by Chris McDonough
diff --git a/docs/designdefense.rst b/docs/designdefense.rst
index 7bc37ac06..bbce3e29c 100644
--- a/docs/designdefense.rst
+++ b/docs/designdefense.rst
@@ -38,13 +38,13 @@ forking is also a Zope derivative.
Implementations of these features were *required* to allow the :app:`Pyramid`
authors to build the bread-and-butter CMS-type systems for customers in the
-way they were accustomed to building them. No other system, save for Zope itself,
-had such features. And Zope itself was beginning to show signs of its age.
+way in which they were accustomed. No other system, save for Zope itself,
+had such features, and Zope itself was beginning to show signs of its age.
We were becoming hampered by consequences of its early design mistakes.
Zope's lack of documentation was also difficult to work around: it was hard
to hire smart people to work on Zope applications, because there was no
comprehensive documentation set to point them at which explained "it all" in
-one consumble place, and it was too large and self-inconsistent to document
+one consumable place, and it was too large and self-inconsistent to document
properly. Before :mod:`repoze.bfg` went under development, its authors
obviously looked around for other frameworks that fit the bill. But no
non-Zope framework did. So we embarked on building :mod:`repoze.bfg`.
@@ -514,7 +514,7 @@ the method is called (if possible) with its argument list filled with values
mentioned therein. TurboGears and Pylons 1.X operate similarly.
Out of the box, :app:`Pyramid` is configured to have none of these features.
-By default, :mod:`pyramid` view callables always accept only ``request`` and
+By default, :app:`Pyramid` view callables always accept only ``request`` and
no other arguments. The rationale: this argument specification matching done
aggressively can be costly, and :app:`Pyramid` has performance as one of its
main goals, so we've decided to make people, by default, obtain information
@@ -594,55 +594,45 @@ requires extensibility because it must be deployed in multiple locations.
Pyramid Is Too Big
------------------
-"The :app:`Pyramid` compressed tarball is almost 2MB. It must be
+"The :app:`Pyramid` compressed tarball is larger than 2MB. It must be
enormous!"
-No. We just ship it with test code and helper templates. Here's a
+No. We just ship it with docs, test code, and scaffolding. Here's a
breakdown of what's included in subdirectories of the package tree:
docs/
- 3.0MB
+ 4.9MB
pyramid/tests/
- 1.1MB
+ 2.0MB
-pyramid/paster_templates/
+pyramid/scaffolds/
- 804KB
+ 460KB
-pyramid/ (except for ``pyramd/tests and pyramid/paster_templates``)
+pyramid/ (except for ``pyramd/tests`` and ``pyramid/scaffolds``)
- 539K
+ 844KB
-The actual :app:`Pyramid` runtime code is about 10% of the total size of the
-tarball omitting docs, helper templates used for package generation, and test
-code. Of the approximately 19K lines of Python code in the package, the code
+Of the approximately 34K lines of Python code in the package, the code
that actually has a chance of executing during normal operation, excluding
-tests and paster template Python files, accounts for approximately 5K lines
-of Python code. This is comparable to Pylons 1.X, which ships with a little
-over 2K lines of Python code, excluding tests.
+tests and scaffolding Python files, accounts for approximately 10K lines.
+
Pyramid Has Too Many Dependencies
---------------------------------
-This is true. At the time of this writing (Pyramid 1.3), the total number of
-Python package distributions that :app:`Pyramid` depends upon transitively is
-if you use Python 3.2 or Python 2.7 is 10. If you use Python 2.6, Pyramid
-will pull in 12 package distributions. This is a lot more than zero package
-distribution dependencies: a metric which various Python microframeworks and
-Django boast.
-
-However, Pyramid 1.2 relied on 15 packages under Python 2.7 and 17 packages
-under Python 2.6, so we've made progress here. A port to Python 3 completed
-in Pyramid 1.3 helped us shed a good number of dependencies by forcing us to
-make better packaging decisions.
+Over time, we've made lots of progress on reducing the number of packaging
+dependencies Pyramid has had. Pyramid 1.2 had 15 of them. Pyramid 1.3 and 1.4
+had 12 of them. The current release as of this writing, Pyramid 1.5, has
+only 7. This number is unlikely to become any smaller.
-In the future, we may also move templating system dependencies out of the
-core and place them in add-on packages, to be included by developers instead
-of by the framework. This would reduce the number of core dependencies by
-about five, leaving us with only five remaining core dependencies.
+A port to Python 3 completed in Pyramid 1.3 helped us shed a good number of
+dependencies by forcing us to make better packaging decisions. Removing
+Chameleon and Mako templating system dependencies in the Pyramid core in 1.5
+let us shed most of the remainder of them.
Pyramid "Cheats" To Obtain Speed
--------------------------------
@@ -774,7 +764,7 @@ content management system (CMS) may have basic functionality that needs to be
extended for a particular deployment. That CMS system may be deployed for
many organizations at many places. Some number of deployments of this CMS
may be deployed centrally by a third party and managed as a group. It's
-useful to be able to extend such a system for each deployment via preordained
+easier to be able to extend such a system for each deployment via preordained
plugpoints than it is to continually keep each software branch of the system
in sync with some upstream source: the upstream developers may change code in
such a way that your changes to the same codebase conflict with theirs in
@@ -802,8 +792,8 @@ such a feature.
main template and the CSS in a separate Python package which defines
overrides.
-- If a deployment needs an application page to do something differently needs
- it to expose more or different information, the deployer may override the
+- If a deployment needs an application page to do something differently, or
+ to expose more or different information, the deployer may override the
view that renders the page within a separate Python package.
- If a deployment needs an additional feature, the deployer may add a view to
@@ -820,7 +810,7 @@ won't regularly need to deal wth meaningless textual merge conflicts that
trivial changes to upstream packages often entail when it comes time to
update the upstream package, because if you extend an application externally,
there just is no textual merge done. Your modifications will also, for
-whatever its worth, be contained in one, canonical, well-defined place.
+whatever it's worth, be contained in one, canonical, well-defined place.
Branching an application and continually merging in order to get new features
and bugfixes is clearly useful. You can do that with a :app:`Pyramid`
@@ -832,7 +822,7 @@ dismiss this feature in favor of branching and merging because applications
written in their framework of choice aren't extensible out of the box in a
comparably fundamental way.
-While :app:`Pyramid` application are fundamentally extensible even if you
+While :app:`Pyramid` applications are fundamentally extensible even if you
don't write them with specific extensibility in mind, if you're moderately
adventurous, you can also take it a step further. If you learn more about
the :term:`Zope Component Architecture`, you can optionally use it to expose
@@ -852,7 +842,7 @@ applications by :app:`Pyramid` are good or bad is mostly pointless. You
needn't take advantage of the extensibility features provided by a particular
:app:`Pyramid` application in order to affect a modification for a particular
set of its deployments. You can ignore the application's extensibility
-plugpoints entirely, and instead use version control branching and merging to
+plugpoints entirely, and use version control branching and merging to
manage application deployment modifications instead, as if you were deploying
an application written using any other web framework.
@@ -1295,6 +1285,9 @@ predictability.
a registry in another module. This has the effect that
double-registrations will never be performed.
+
+.. _routes_need_ordering:
+
Routes Need Relative Ordering
+++++++++++++++++++++++++++++
@@ -1458,7 +1451,7 @@ code below:
def afunc():
for i in range(10):
- print i
+ print(i)
By its nature, the *request* object created as the result of a WSGI server's
call into a long-lived web framework cannot be global, because the lifetime
@@ -1656,7 +1649,7 @@ If you can understand this hello world program, you can use Pyramid:
server = make_server('0.0.0.0', 8080, app)
server.serve_forever()
-Pyramid has ~ 650 pages of documentation (printed), covering topics from the
+Pyramid has ~ 700 pages of documentation (printed), covering topics from the
very basic to the most advanced. *Nothing* is left undocumented, quite
literally. It also has an *awesome*, very helpful community. Visit the
#pyramid IRC channel on freenode.net (irc://freenode.net#pyramid) and see.
@@ -1678,7 +1671,7 @@ was written to address these issues.
If it's Zope3-the-web-framework, Pyramid is *definitely* not that. Making
use of lots of Zope 3 technologies is territory already staked out by the
:term:`Grok` project. Save for the obvious fact that they're both web
-frameworks, :mod:`Pyramid` is very, very different than Grok. Grok exposes
+frameworks, :app:`Pyramid` is very, very different than Grok. Grok exposes
lots of Zope technologies to end users. On the other hand, if you need to
understand a Zope-only concept while using Pyramid, then we've failed on some
very basic axis.
@@ -1691,7 +1684,7 @@ some sort of monolithic thing, and a lot of its software is usable
externally. And while it's not really the job of this document to defend it,
Zope has been around for over 10 years and has an incredibly large, active
community. If you don't believe this,
-http://taichino.appspot.com/pypi_ranking/authors is an eye-opening reality
+http://pypi-ranking.info/author is an eye-opening reality
check.
Love Simplicity
diff --git a/docs/foreword.rst b/docs/foreword.rst
index aa8d7c77b..cc8271bdf 100644
--- a/docs/foreword.rst
+++ b/docs/foreword.rst
@@ -1,3 +1,5 @@
+:orphan:
+
Foreword
========
diff --git a/docs/glossary.rst b/docs/glossary.rst
index 241f951d6..406b81778 100644
--- a/docs/glossary.rst
+++ b/docs/glossary.rst
@@ -156,9 +156,9 @@ Glossary
resource
An object representing a node in the :term:`resource tree` of an
- application. If :mod:`traversal` is used, a resource is an element in
+ application. If :term:`traversal` is used, a resource is an element in
the resource tree traversed by the system. When traversal is used, a
- resource becomes the :term:`context` of a :term:`view`. If :mod:`url
+ resource becomes the :term:`context` of a :term:`view`. If :term:`url
dispatch` is used, a single resource is generated for each request and
is used as the context resource of a view.
@@ -505,15 +505,20 @@ Glossary
available as its ``__parent__`` attribute.
root factory
- The "root factory" of a :app:`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 :app:`Pyramid` during the construction of a
- :term:`Configurator`. 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 :term:`URL dispatch` for
- all URL-to-view code mappings.
+ The "root factory" of a :app:`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 :app:`Pyramid` during the
+ construction of a :term:`Configurator`. If a root factory is not
+ supplied, the application creates a default root object using the
+ :term:`default root factory`.
+
+ default root factory
+ If an application does not register a :term:`root factory` at Pyramid
+ configuration time, a *default* root factory is used to created the
+ default root object. Use of the default root object is useful in
+ application which use :term:`URL dispatch` for all URL-to-view code
+ mappings, and does not (knowingly) use traversal otherwise.
SQLAlchemy
`SQLAlchemy <http://www.sqlalchemy.org/>`_ is an object
@@ -698,7 +703,7 @@ Glossary
:app:`Pyramid` runs on GAE.
Venusian
- `Venusian <http://docs.repoze.org/venusian>`_ is a library which
+ :ref:`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
@@ -798,9 +803,8 @@ Glossary
:term:`Internationalization`.
renderer globals
- Values injected as names into a renderer based on application
- policy. See :ref:`adding_renderer_globals` for more
- information.
+ Values injected as names into a renderer by a
+ :class:`pyramid.event.BeforeRender` event.
response callback
A user-defined callback executed by the :term:`router` at a
@@ -1010,7 +1014,8 @@ Glossary
Green Unicorn
Aka ``gunicorn``, a fast :term:`WSGI` server that runs on UNIX under
- Python 2.6+ or Python 3.1+. See http://gunicorn.org/ for detailed information.
+ Python 2.6+ or Python 3.1+. See http://gunicorn.org/ for detailed
+ information.
predicate factory
A callable which is used by a third party during the registration of a
@@ -1018,3 +1023,13 @@ Glossary
system. See :ref:`registering_thirdparty_predicates` for more
information.
+ add-on
+ A Python :term:`distribution` that uses Pyramid's extensibility
+ to plug into a Pyramid application and provide extra,
+ configurable services.
+
+ pyramid_redis_sessions
+ A package by Eric Rasmussen which allows you to store Pyramid session
+ data in a Redis database. See
+ https://pypi.python.org/pypi/pyramid_redis_sessions for more information.
+
diff --git a/docs/index.rst b/docs/index.rst
index bc711f8ff..78a00966d 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,11 +1,11 @@
.. _index:
-=================================================
-The Pyramid Web Application Development Framework
-=================================================
+=========================
+The Pyramid Web Framework
+=========================
-:app:`Pyramid` is a small, fast, down-to-earth Python web application
-development framework. It is developed as part of the `Pylons Project
+:app:`Pyramid` is a small, fast, down-to-earth Python web framework. It
+is developed as part of the `Pylons Project
<http://docs.pylonsproject.org/>`_. It is licensed under a `BSD-like license
<http://repoze.org/license.html>`_.
@@ -20,7 +20,8 @@ After you install :app:`Pyramid` and run this application, when you visit
See :ref:`firstapp_chapter` for a full explanation of how this application
works. Read the :ref:`html_narrative_documentation` to understand how
:app:`Pyramid` is designed to scale from simple applications like this to
-very large web applications.
+very large web applications. To just dive in headfirst, read the
+:doc:`quick_tour`.
Front Matter
============
@@ -31,21 +32,45 @@ Front Matter
copyright.rst
conventions.rst
-What's New
-==========
+.. _html_getting_started:
+
+Getting Started
+===============
+
+If you are new to Pyramid, we have a few resources that can help you get
+up to speed right away.
.. toctree::
- :maxdepth: 1
+ :hidden:
+
+ quick_tour
+ quick_tutorial/index
+
+* :doc:`quick_tour` goes through the major features in Pyramid, covering
+ a little about a lot.
+
+* :doc:`quick_tutorial/index` does the same, but in a tutorial format:
+ deeper treatment of each topic and with working code.
+
+* To see a minimal Pyramid web application, check out
+ :ref:`firstapp_chapter`.
+
+* For help getting Pyramid set up, try
+ :ref:`installing_chapter`.
+
+* Like learning by example? Visit the official
+ :doc:`wiki tutorial <../tutorials/wiki2/index>` as well as the
+ community-contributed
+ :ref:`Pyramid tutorials <tutorials:pyramid-tutorials>`, which include
+ a :ref:`single file tasks tutorial <tutorials:single-file-tutorial>`.
+
+* Need help? See :ref:`Support and
+ Development <support-and-development>`.
- whatsnew-1.4
- whatsnew-1.3
- whatsnew-1.2
- whatsnew-1.1
- whatsnew-1.0
.. _html_narrative_documentation:
-Narrative documentation
+Narrative Documentation
=======================
Narrative documentation in chapter form explaining how to use
@@ -106,11 +131,12 @@ platforms.
tutorials/wiki2/index.rst
tutorials/wiki/index.rst
- tutorials/bfg/index.rst
tutorials/modwsgi/index.rst
+.. _html_api_documentation:
+
API Documentation
-==================
+=================
Comprehensive reference material for every public API exposed by :app:`Pyramid`:
@@ -126,6 +152,12 @@ Change History
.. toctree::
:maxdepth: 1
+ whatsnew-1.5
+ whatsnew-1.4
+ whatsnew-1.3
+ whatsnew-1.2
+ whatsnew-1.1
+ whatsnew-1.0
changes
Design Documents
@@ -136,51 +168,7 @@ Design Documents
designdefense
-Sample Applications
-===================
-
-.. note::
-
- These applications run only on Python 2.x, and so do some of their
- dependencies.
-
-`cluegun <https://github.com/Pylons/cluegun>`_ is a simple pastebin
-application based on Rocky Burt's `ClueBin
-<http://pypi.python.org/pypi/ClueBin/0.2.3>`_. It demonstrates form
-processing, security, and the use of :term:`ZODB` within a :app:`Pyramid`
-application. Check this application out via:
-
-.. code-block:: text
-
- git clone git://github.com/Pylons/cluegun.git
-
-`virginia <https://github.com/Pylons/virginia>`_ is a very simple dynamic
-file rendering application. It is willing to render structured text
-documents, HTML documents, and images from a filesystem directory.
-It's also a good example of :term:`traversal`. An
-earlier version of this application runs the `repoze.org
-<http://repoze.org>`_ website. Check this application out via:
-
-.. code-block:: text
-
- git clone git://github.com/Pylons/virginia.git
-
-`shootout <https://github.com/Pylons/shootout>`_ is an example "idea
-competition" application by Carlos de la Guardia and Lukasz Fidosz. It
-demonstrates :term:`URL dispatch`, simple authentication, integration
-with `SQLAlchemy <http://www.sqlalchemy.org/>`_ and ``pyramid_simpleform``.
-Check this application out of version control via:
-
-.. code-block:: text
-
- git clone git://github.com/Pylons/shootout.git
-
-`KARL <http://karlproject.org>`_ is a moderately-sized application (roughly
-80K lines of Python code) built on top of :app:`Pyramid`. It is an open
-source web system for collaboration, organizational intranets, and knowledge
-management. It provides facilities for wikis, calendars, manuals, searching,
-tagging, commenting, and file uploads. See the `KARL site
-<http://karlproject.org>`_ for download and installation details.
+.. _support-and-development:
Support and Development
=======================
@@ -216,12 +204,8 @@ Index and Glossary
* :ref:`search`
-.. add glossary, foreword, and latexindex in a hidden toc to avoid warnings
-
.. toctree::
:hidden:
glossary
- foreword.rst
- latexindex.rst
diff --git a/docs/latexindex.rst b/docs/latexindex.rst
index 6bb875f73..c4afff212 100644
--- a/docs/latexindex.rst
+++ b/docs/latexindex.rst
@@ -1,8 +1,10 @@
+:orphan:
+
.. _latexindex:
-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
-The :app:`Pyramid` Web Application Framework
-@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+=========================
+The Pyramid Web Framework
+=========================
.. frontmatter::
@@ -28,8 +30,8 @@ Narrative Documentation
narr/introduction
narr/install
- narr/configuration
narr/firstapp
+ narr/configuration
narr/project
narr/startup
narr/router
@@ -50,6 +52,7 @@ Narrative Documentation
narr/vhosting
narr/testing
narr/resources
+ narr/hellotraversal
narr/muchadoabouttraversal
narr/traversal
narr/security
@@ -60,6 +63,8 @@ Narrative Documentation
narr/extending
narr/advconfig
narr/extconfig
+ narr/scaffolding
+ narr/upgrading
narr/threadlocals
narr/zca
@@ -71,42 +76,20 @@ Tutorials
.. toctree::
:maxdepth: 1
- tutorials/wiki/index.rst
tutorials/wiki2/index.rst
- tutorials/bfg/index.rst
+ tutorials/wiki/index.rst
tutorials/modwsgi/index.rst
-.. _api_reference:
+.. _api_documentation:
-API Reference
-@@@@@@@@@@@@@
+API Documentation
+@@@@@@@@@@@@@@@@@
.. toctree::
:maxdepth: 1
+ :glob:
- api/authorization
- api/authentication
- api/config
- api/events
- api/exceptions
- api/httpexceptions
- api/i18n
- api/interfaces
- api/location
- api/paster
- api/registry
- api/renderers
- api/request
- api/response
- api/scripting
- api/security
- api/settings
- api/testing
- api/threadlocal
- api/traversal
- api/url
- api/view
- api/wsgi
+ api/*
.. backmatter::
diff --git a/docs/narr/MyProject/myproject/__init__.py b/docs/narr/MyProject/myproject/__init__.py
index 6c512f52f..ad5ecbc6f 100644
--- a/docs/narr/MyProject/myproject/__init__.py
+++ b/docs/narr/MyProject/myproject/__init__.py
@@ -5,6 +5,7 @@ def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.scan()
diff --git a/docs/narr/MyProject/myproject/templates/mytemplate.pt b/docs/narr/MyProject/myproject/templates/mytemplate.pt
index 0bfac946e..0fccba624 100644
--- a/docs/narr/MyProject/myproject/templates/mytemplate.pt
+++ b/docs/narr/MyProject/myproject/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/narr/MyProject/setup.py b/docs/narr/MyProject/setup.py
index 6969c73e7..a23f46c91 100644
--- a/docs/narr/MyProject/setup.py
+++ b/docs/narr/MyProject/setup.py
@@ -10,6 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
'pyramid_debugtoolbar',
'waitress',
]
diff --git a/docs/narr/advconfig.rst b/docs/narr/advconfig.rst
index 1b8e33de3..d3431e39e 100644
--- a/docs/narr/advconfig.rst
+++ b/docs/narr/advconfig.rst
@@ -302,7 +302,6 @@ These are the methods of the configurator which provide conflict detection:
:meth:`~pyramid.config.Configurator.set_view_mapper`,
:meth:`~pyramid.config.Configurator.set_authentication_policy`,
:meth:`~pyramid.config.Configurator.set_authorization_policy`,
-:meth:`~pyramid.config.Configurator.set_renderer_globals_factory`,
:meth:`~pyramid.config.Configurator.set_locale_negotiator`,
:meth:`~pyramid.config.Configurator.set_default_permission`,
:meth:`~pyramid.config.Configurator.add_traverser`,
diff --git a/docs/narr/assets.rst b/docs/narr/assets.rst
index 26b3e3a92..b0a8d18b0 100644
--- a/docs/narr/assets.rst
+++ b/docs/narr/assets.rst
@@ -227,14 +227,14 @@ API to generate them for you. For example:
.. code-block:: python
:linenos:
- from pyramid.chameleon_zpt import render_template_to_response
+ from pyramid.renderers import render_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)
+ return render_to_response('templates/my_template.pt',
+ dict(css_url=css_url, js_url=js_url),
+ request=request)
If the request "application URL" of the running system is
``http://example.com``, the ``css_url`` generated above would be:
@@ -336,7 +336,9 @@ your application root as below.
from pyramid.static import static_view
static_view = static_view('/path/to/static/dir', use_subpath=True)
-.. note:: For better cross-system flexibility, use an :term:`asset
+.. note::
+
+ For better cross-system flexibility, use an :term:`asset
specification` as the argument to :class:`~pyramid.static.static_view`
instead of a physical absolute filesystem path, e.g. ``mypackage:static``
instead of ``/path/to/mypackage/static``.
@@ -432,9 +434,9 @@ feature, a :term:`Configurator` API exists named
:meth:`pyramid.config.Configurator.override_asset`. This API allows you to
*override* the following kinds of assets defined in any Python package:
-- Individual :term:`Chameleon` templates.
+- Individual template files.
-- A directory containing multiple Chameleon templates.
+- A directory containing multiple template files.
- Individual static files served up by an instance of the
``pyramid.static.static_view`` helper class.
@@ -460,8 +462,8 @@ can override a single asset. For example:
:linenos:
config.override_asset(
- to_override='some.package:templates/mytemplate.pt',
- override_with='another.package:othertemplates/anothertemplate.pt')
+ to_override='some.package:templates/mytemplate.pt',
+ override_with='another.package:othertemplates/anothertemplate.pt')
The string value passed to both ``to_override`` and ``override_with`` sent to
the ``override_asset`` API is called an :term:`asset specification`. The
diff --git a/docs/narr/commandline.rst b/docs/narr/commandline.rst
index e1347f3ca..58b9bdd21 100644
--- a/docs/narr/commandline.rst
+++ b/docs/narr/commandline.rst
@@ -474,6 +474,30 @@ input of the ``prequest`` process is used as the ``POST`` body::
$ $VENV/bin/prequest -mPOST development.ini / < somefile
+Showing All Installed Distributions and their Versions
+------------------------------------------------------
+
+.. versionadded:: 1.5
+
+You can use the ``pdistreport`` command to show the Pyramid version in use, the
+Python version in use, and all installed versions of Python distributions in
+your Python environment::
+
+ $ $VENV/bin/pdistreport
+ Pyramid version: 1.5dev
+ Platform Linux-3.2.0-51-generic-x86_64-with-debian-wheezy-sid
+ Packages:
+ authapp 0.0
+ /home/chrism/projects/foo/src/authapp
+ beautifulsoup4 4.1.3
+ /home/chrism/projects/foo/lib/python2.7/site-packages/beautifulsoup4-4.1.3-py2.7.egg
+ ... more output ...
+
+``pdistreport`` takes no options. Its output is useful to paste into a
+pastebin when you are having problems and need someone with more familiarity
+with Python packaging and distribution than you have to look at your
+environment.
+
.. _writing_a_script:
Writing a Script
@@ -514,7 +538,7 @@ representing Pyramid your application configuration as a single argument:
from pyramid.paster import bootstrap
env = bootstrap('/path/to/my/development.ini')
- print env['request'].route_url('home')
+ print(env['request'].route_url('home'))
:func:`pyramid.paster.bootstrap` returns a dictionary containing
framework-related information. This dictionary will always contain a
@@ -582,7 +606,7 @@ to load instead of ``main``:
from pyramid.paster import bootstrap
env = bootstrap('/path/to/my/development.ini#another')
- print env['request'].route_url('home')
+ print(env['request'].route_url('home'))
The above example specifies the ``another`` ``app``, ``pipeline``, or
``composite`` section of your PasteDeploy configuration file. The ``app``
@@ -619,7 +643,7 @@ the desired request and passing it into :func:`~pyramid.paster.bootstrap`:
request = Request.blank('/', base_url='https://example.com/prefix')
env = bootstrap('/path/to/my/development.ini#another', request=request)
- print env['request'].application_url
+ print(env['request'].application_url)
# will print 'https://example.com/prefix'
Now you can readily use Pyramid's APIs for generating URLs:
diff --git a/docs/narr/environment.rst b/docs/narr/environment.rst
index e059acc4e..f0c0c18fe 100644
--- a/docs/narr/environment.rst
+++ b/docs/narr/environment.rst
@@ -302,7 +302,7 @@ Ideally, you won't need to use the ``pyramid.tweens`` setting at all. Tweens
are generally ordered and included "implicitly" when an add-on package which
registers a tween is "included". Packages are included when you name a
``pyramid.includes`` setting in your configuration or when you call
-:meth:`pyramid.config.Configuration.include`.
+:meth:`pyramid.config.Configurator.include`.
Authors of included add-ons provide "implicit" tween configuration ordering
hints to Pyramid when their packages are included. However, the implicit
diff --git a/docs/narr/events.rst b/docs/narr/events.rst
index 929208083..2accb3dbe 100644
--- a/docs/narr/events.rst
+++ b/docs/narr/events.rst
@@ -26,7 +26,7 @@ subscriber is a function that accepts a single argument named `event`:
:linenos:
def mysubscriber(event):
- print event
+ print(event)
The above is a subscriber that simply prints the event to the console
when it's called.
@@ -53,7 +53,7 @@ method (see also :term:`Configurator`):
from subscribers import mysubscriber
- # "config" below is assumed to be an instance of a
+ # "config" below is assumed to be an instance of a
# pyramid.config.Configurator object
config.add_subscriber(mysubscriber, NewRequest)
@@ -77,7 +77,7 @@ type via the :func:`pyramid.events.subscriber` function.
@subscriber(NewRequest)
def mysubscriber(event):
- event.request.foo = 1
+ event.request.foo = 1
When the :func:`~pyramid.events.subscriber` decorator is used a
:term:`scan` must be performed against the package containing the
@@ -113,10 +113,10 @@ your application like so:
:linenos:
def handle_new_request(event):
- print 'request', event.request
+ print('request', event.request)
def handle_new_response(event):
- print 'response', event.response
+ print('response', event.response)
You may configure these functions to be called at the appropriate
times by adding the following code to your application's
@@ -150,3 +150,86 @@ application, because the interface defined at
:class:`pyramid.interfaces.INewResponse` says it must
(:class:`pyramid.events.NewResponse` objects also have a ``request``).
+.. _custom_events:
+
+Creating Your Own Events
+------------------------
+
+In addition to using the events that the Pyramid framework creates,
+you can create your own events for use in your application. This can
+be useful to decouple parts of your application.
+
+For example, suppose your application has to do many things when a new
+document is created. Rather than putting all this logic in the view
+that creates the document, you can create the document in your view
+and then fire a custom event. Subscribers to the custom event can take
+other actions, such as indexing the document, sending email, or
+sending a message to a remote system.
+
+An event is simply an object. There are no required attributes or
+method for your custom events. In general, your events should keep
+track of the information that subscribers will need. Here are some
+example custom event classes:
+
+.. code-block:: python
+ :linenos:
+
+ class DocCreated(object):
+ def __init__(self, doc, request):
+ self.doc = doc
+ self.request = request
+
+ class UserEvent(object):
+ def __init__(self, user):
+ self.user = user
+
+ class UserLoggedIn(UserEvent):
+ pass
+
+Some Pyramid applications choose to define custom events classes in an
+``events`` module.
+
+You can subscribe to custom events in the same way that you subscribe
+to Pyramid events -- either imperatively or with a decorator. You can
+also use custom events with :ref:`subscriber predicates
+<subscriber_predicates>`. Here's an example of subscribing to a custom
+event with a decorator:
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.events import subscriber
+ from .events import DocCreated
+ from .index import index_doc
+
+ @subscriber(DocCreated)
+ def index_doc(event):
+ # index the document using our application's index_doc function
+ index_doc(event.doc, event.request)
+
+The above example assumes that the application defines a
+``DocCreated`` event class and an ``index_doc`` function.
+
+To fire your custom events use the
+:meth:`pyramid.registry.Registry.notify` method, which is most often
+accessed as ``request.registry.notify``. For example:
+
+.. code-block:: python
+ :linenos:
+
+ from .events import DocCreated
+
+ def new_doc_view(request):
+ doc = MyDoc()
+ event = DocCreated(doc, request)
+ request.registry.notify(event)
+ return {'document': doc}
+
+This example view will notify all subscribers to the custom
+``DocCreated`` event.
+
+Note that when you fire an event, all subscribers are run
+synchronously so it's generally not a good idea
+to create event handlers that may take a long time to run. Although
+event handlers could be used as a central place to spawn tasks on your
+own message queues.
diff --git a/docs/narr/extconfig.rst b/docs/narr/extconfig.rst
index 659056952..6587aef92 100644
--- a/docs/narr/extconfig.rst
+++ b/docs/narr/extconfig.rst
@@ -55,7 +55,7 @@ method of the Configurator:
:linenos:
def mysubscriber(event):
- print event.request
+ print(event.request)
config.add_newrequest_subscriber(mysubscriber)
@@ -79,7 +79,7 @@ able to install it and subsequently do:
:linenos:
def mysubscriber(event):
- print event.request
+ print(event.request)
from pyramid.config import Configurator
config = Configurator()
diff --git a/docs/narr/hooks.rst b/docs/narr/hooks.rst
index 77c66b0d2..0c450fad7 100644
--- a/docs/narr/hooks.rst
+++ b/docs/narr/hooks.rst
@@ -14,8 +14,8 @@ in various ways.
Changing the Not Found View
---------------------------
-When :app:`Pyramid` can't map a URL to view code, it invokes a :term:`not
-found view`, which is a :term:`view callable`. The default Not Found View
+When :app:`Pyramid` can't map a URL to view code, it invokes a :term:`Not
+Found View`, which is a :term:`view callable`. The default Not Found View
can be overridden through application configuration.
If your application uses :term:`imperative configuration`, you can replace
@@ -25,15 +25,17 @@ the Not Found View by using the
.. code-block:: python
:linenos:
- from helloworld.views import notfound
- config.add_notfound_view(notfound)
+ def notfound(request):
+ return Response('Not Found, dude', status='404 Not Found')
+
+ def main(globals, **settings):
+ config = Configurator()
+ config.add_notfound_view(notfound)
-Replace ``helloworld.views.notfound`` with a reference to the :term:`view
-callable` you want to use to represent the Not Found View. The :term:`not
-found view` callable is a view callable like any other.
+The :term:`Not Found View` callable is a view callable like any other.
If your application instead uses :class:`pyramid.view.view_config` decorators
-and a :term:`scan`, you can replace the Not Found view by using the
+and a :term:`scan`, you can replace the Not Found View by using the
:class:`pyramid.view.notfound_view_config` decorator:
.. code-block:: python
@@ -46,8 +48,8 @@ and a :term:`scan`, you can replace the Not Found view by using the
return Response('Not Found, dude', status='404 Not Found')
def main(globals, **settings):
- config = Configurator()
- config.scan()
+ config = Configurator()
+ config.scan()
This does exactly what the imperative example above showed.
@@ -154,12 +156,12 @@ forbidden view:
.. code-block:: python
:linenos:
- from helloworld.views import forbidden_view
- from pyramid.httpexceptions import HTTPForbidden
- config.add_forbidden_view(forbidden_view)
+ def forbidden(request):
+ return Response('forbidden')
-Replace ``helloworld.views.forbidden_view`` with a reference to the Python
-:term:`view callable` you want to use to represent the Forbidden view.
+ def main(globals, **settings):
+ config = Configurator()
+ config.add_forbidden_view(forbidden_view)
If instead you prefer to use decorators and a :term:`scan`, you can use the
:class:`pyramid.view.forbidden_view_config` decorator to mark a view callable
@@ -289,7 +291,7 @@ actually execute the function until accessed.
return sum(args)
def prop(request):
- print "getting the property"
+ print("getting the property")
return "the property"
config = Configurator()
@@ -330,7 +332,7 @@ Here is an example of passing a class to ``Configurator.add_request_method``:
# use @property if you don't want to cache the result
@reify
def prop(self):
- print "getting the property"
+ print("getting the property")
return "the property"
config = Configurator()
@@ -370,10 +372,8 @@ that can be used for this purpose. For example:
def add_global(event):
event['mykey'] = 'foo'
-An object of this type is sent as an event just before a :term:`renderer` is
-invoked (but *after* the application-level renderer globals factory added via
-:class:`~pyramid.config.Configurator.set_renderer_globals_factory`, if any,
-has injected its own keys into the renderer globals dictionary).
+An object of this type is sent as an event just before a :term:`renderer`
+is invoked.
If a subscriber attempts to add a key that already exist in the renderer
globals dictionary, a :exc:`KeyError` is raised. This limitation is enforced
@@ -415,66 +415,6 @@ your view callable, like so:
See the API documentation for the :class:`~pyramid.events.BeforeRender` event
interface at :class:`pyramid.interfaces.IBeforeRender`.
-Another (deprecated) mechanism which allows event subscribers more control
-when adding renderer global values exists in :ref:`adding_renderer_globals`.
-
-.. index::
- single: adding renderer globals
-
-.. _adding_renderer_globals:
-
-Adding Renderer Globals (Deprecated)
-------------------------------------
-
-.. deprecated:: 1.1
- An alternative mechanism which allows event subscribers to add renderer
- global values is documented in :ref:`beforerender_event`.
-
-Whenever :app:`Pyramid` handles a request to perform a rendering (after a
-view with a ``renderer=`` configuration attribute is invoked, or when any of
-the methods beginning with ``render`` within the :mod:`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 :app:`Pyramid` will call every time a renderer is invoked can
-be added by passing a ``renderer_globals_factory`` argument to the
-constructor of the :term:`configurator`. This callback can either be a
-callable object or a :term:`dotted Python name` representing such a callable.
-
-.. code-block:: python
- :linenos:
-
- def renderer_globals_factory(system):
- return {'a': 1}
-
- config = Configurator(
- renderer_globals_factory=renderer_globals_factory)
-
-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
-:ref:`renderer_system_values` for description of the values present in the
-system dictionary.
-
-If you're doing imperative configuration, and you'd rather do it after you've
-already constructed a :term:`configurator` it can also be registered via the
-:meth:`pyramid.config.Configurator.set_renderer_globals_factory` method:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.config import Configurator
-
- def renderer_globals_factory(system):
- return {'a': 1}
-
- config = Configurator()
- config.set_renderer_globals_factory(renderer_globals_factory)
-
-
.. index::
single: response callback
@@ -512,7 +452,7 @@ callback will be an exception object instead of its default value of
``None``.
Response callbacks are called in the order they're added
-(first-to-most-recently-added). All response callbacks are called *after*
+(first-to-most-recently-added). All response callbacks are called *before*
the :class:`~pyramid.events.NewResponse` event is sent. Errors raised by
response callbacks are not handled specially. They will be propagated to the
caller of the :app:`Pyramid` router application.
@@ -673,7 +613,7 @@ traverser.
If you've added a traverser, you can change how
:meth:`~pyramid.request.Request.resource_url` generates a URL for a specific
type of resource by adding a call to
-:meth:`pyramid.config.add_resource_url_adapter`.
+:meth:`pyramid.config.Configurator.add_resource_url_adapter`.
For example:
@@ -1382,9 +1322,11 @@ The first argument to :meth:`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:
+The second argument is a view or route predicate factory, or a :term:`dotted
+Python name` which refers to 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:
.. code-block:: python
:linenos:
diff --git a/docs/narr/hybrid.rst b/docs/narr/hybrid.rst
index 1773a6b8c..a29ccb2ac 100644
--- a/docs/narr/hybrid.rst
+++ b/docs/narr/hybrid.rst
@@ -549,3 +549,105 @@ 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.
+
+.. index::
+ pair: hybrid urls; generating
+
+.. _generating_hybrid_urls:
+
+Generating Hybrid URLs
+----------------------
+
+.. versionadded:: 1.5
+
+The :meth:`pyramid.request.Request.resource_url` method and the
+:meth:`pyramid.request.Request.resource_path` method both accept optional
+keyword arguments that make it easier to generate route-prefixed URLs that
+contain paths to traversal resources:``route_name``, ``route_kw``, and
+``route_remainder_name``.
+
+Any route that has a pattern that contains a ``*remainder`` pattern (any
+stararg remainder pattern, such as ``*traverse`` or ``*subpath`` or ``*fred``)
+can be used as the target name for ``request.resource_url(..., route_name=)``
+and ``request.resource_path(..., route_name=)``.
+
+For example, let's imagine you have a route defined in your Pyramid application
+like so:
+
+.. code-block:: python
+
+ config.add_route('mysection', '/mysection*traverse')
+
+If you'd like to generate the URL ``http://example.com/mysection/a/``, you can
+use the following incantation, assuming that the variable ``a`` below points to
+a resource that is a child of the root with a ``__name__`` of ``a``:
+
+.. code-block:: python
+
+ request.resource_url(a, route_name='mysection')
+
+You can generate only the path portion ``/mysection/a/`` assuming the same:
+
+.. code-block:: python
+
+ request.resource_path(a, route_name='mysection')
+
+The path is virtual host aware, so if the ``X-Vhm-Root`` environ variable is
+present in the request, and it's set to ``/a``, the above call to
+``request.resource_url`` would generate ``http://example.com/mysection/``
+and the above call to ``request.resource_path`` would generate ``/mysection/``.
+See :ref:`virtual_root_support` for more information.
+
+If the route you're trying to use needs simple dynamic part values to be filled
+in to succesfully generate the URL, you can pass these as the ``route_kw``
+argument to ``resource_url`` and ``resource_path``. For example, assuming that
+the route definition is like so:
+
+.. code-block:: python
+
+ config.add_route('mysection', '/{id}/mysection*traverse')
+
+You can pass ``route_kw`` in to fill in ``{id}`` above:
+
+.. code-block:: python
+
+ request.resource_url(a, route_name='mysection', route_kw={'id':'1'})
+
+If you pass ``route_kw`` but do not pass ``route_name``, ``route_kw`` will
+be ignored.
+
+By default this feature works by calling ``route_url`` under the hood,
+and passing the value of the resource path to that function as ``traverse``.
+If your route has a different ``*stararg`` remainder name (such as
+``*subpath``), you can tell ``resource_url`` or ``resource_path`` to use that
+instead of ``traverse`` by passing ``route_remainder_name``. For example,
+if you have the following route:
+
+.. code-block:: python
+
+ config.add_route('mysection', '/mysection*subpath')
+
+You can fill in the ``*subpath`` value using ``resource_url`` by doing:
+
+.. code-block:: python
+
+ request.resource_path(a, route_name='mysection',
+ route_remainder_name='subpath')
+
+If you pass ``route_remainder_name`` but do not pass ``route_name``,
+``route_remainder_name`` will be ignored.
+
+If you try to use ``resource_path`` or ``resource_url`` when the ``route_name``
+argument points at a route that does not have a remainder stararg, an error
+will not be raised, but the generated URL will not contain any remainder
+information either.
+
+All other values that are normally passable to ``resource_path`` and
+``resource_url`` (such as ``query``, ``anchor``, ``host``, ``port``, and
+positional elements) work as you might expect in this configuration.
+
+Note that this feature is incompatible with the ``__resource_url__`` feature
+(see :ref:`overriding_resource_url_generation`) implemented on resource
+objects. Any ``__resource_url__`` supplied by your resource will be ignored
+when you pass ``route_name``.
+
diff --git a/docs/narr/i18n.rst b/docs/narr/i18n.rst
index 74765f8e2..b62c16ff0 100644
--- a/docs/narr/i18n.rst
+++ b/docs/narr/i18n.rst
@@ -495,7 +495,6 @@ translations will be available to :app:`Pyramid`.
.. index::
single: localizer
- single: get_localizer
single: translation
single: pluralization
@@ -503,19 +502,17 @@ Using a Localizer
-----------------
A :term:`localizer` is an object that allows you to perform translation or
-pluralization "by hand" in an application. You may use the
-:func:`pyramid.i18n.get_localizer` function to obtain a :term:`localizer`.
-This function will return either the localizer object implied by the active
-:term:`locale negotiator` or a default localizer object if no explicit locale
-negotiator is registered.
+pluralization "by hand" in an application. You may use the
+:attr:`pyramid.request.Request.localizer` attribute to obtain a
+:term:`localizer`. The localizer object will be configured to produce
+translations implied by the active :term:`locale negotiator` or a default
+localizer object if no explicit locale negotiator is registered.
.. code-block:: python
:linenos:
- from pyramid.i18n import get_localizer
-
def aview(request):
- locale = get_localizer(request)
+ localizer = request.localizer
.. note::
@@ -538,22 +535,20 @@ translation in a view component of an application might look like so:
.. code-block:: python
:linenos:
- 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)
+ localizer = request.localizer
translated = localizer.translate(ts) # translation string
# ... use translated ...
-The :func:`~pyramid.i18n.get_localizer` function will return a
-:class:`pyramid.i18n.Localizer` object bound to the locale name
-represented by the request. The translation returned from its
-:meth:`pyramid.i18n.Localizer.translate` method will depend on the
-``domain`` attribute of the provided translation string as well as the
+The ``request.localizer`` attribute will be a :class:`pyramid.i18n.Localizer`
+object bound to the locale name represented by the request. The translation
+returned from its :meth:`pyramid.i18n.Localizer.translate` method will depend
+on the ``domain`` attribute of the provided translation string as well as the
locale of the localizer.
.. note::
@@ -579,32 +574,63 @@ signature:
def pluralize(singular, plural, n, domain=None, mapping=None):
...
-The ``singular`` and ``plural`` arguments should each be a Unicode
-value representing a :term:`message identifier`. ``n`` should be an
-integer. ``domain`` should be a :term:`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
-:term:`translation string` objects, but the domain and mapping
-information attached to those objects is ignored.
+The simplest case is the ``singular`` and ``plural`` arguments being passed as
+unicode literals. This returns the appropriate literal according to the locale
+pluralization rules for the number ``n``, and interpolates ``mapping``.
.. code-block:: python
:linenos:
- from pyramid.i18n import get_localizer
-
def aview(request):
- localizer = get_localizer(request)
+ localizer = request.localizer
translated = localizer.pluralize('Item', 'Items', 1, 'mydomain')
# ... use translated ...
+However, for support of other languages, the ``singular`` argument should
+be a Unicode value representing a :term:`message identifier`. In this
+case the ``plural`` value is ignored.
+``domain`` should be a :term:`translation domain`, and
+``mapping`` should be a dictionary that is used for *replacement
+value* interpolation of the translated string.
+
+The value of ``n`` will be used to find the appropriate plural form for the
+current language and ``pluralize`` will return a Unicode translation for the
+message id ``singular``. The message file must have defined ``singular`` as a
+translation with plural forms.
+
+The argument provided as ``singular`` may be a :term:`translation string`
+object, but the domain and mapping information attached is ignored.
+
+.. code-block:: python
+ :linenos:
+
+ def aview(request):
+ localizer = request.localizer
+ num = 1
+ translated = localizer.pluralize(
+ _('item_plural', default="${number} items"),
+ None, num, 'mydomain', mapping={'number':num}
+ )
+
+The corresponding message catalog must have language plural definitions and
+plural alternatives set.
+
+.. code-block:: text
+ :linenos:
+
+ "Plural-Forms: nplurals=3; plural=n==0 ? 0 : n==1 ? 1 : 2;"
+
+ msgid "item_plural"
+ msgid_plural ""
+ msgstr[0] "No items"
+ msgstr[1] "${number} item"
+ msgstr[2] "${number} items"
+
+More information on complex plurals can be found in the `gettext documentation
+<https://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Plural-forms.html>`_.
+
.. index::
single: locale name
- single: get_locale_name
single: negotiate_locale_name
.. _obtaining_the_locale_name:
@@ -613,25 +639,23 @@ Obtaining the Locale Name for a Request
---------------------------------------
You can obtain the locale name related to a request by using the
-:func:`pyramid.i18n.get_locale_name` function.
+:func:`pyramid.request.Request.locale_name` attribute of the request.
.. code-block:: python
:linenos:
- from pyramid.i18n import get_locale_name
-
def aview(request):
- locale_name = get_locale_name(request)
+ locale_name = request.locale_name
-This returns the locale name negotiated by the currently active
-:term:`locale negotiator` or the :term:`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
-:ref:`default_locale_name_setting`.
+The locale name of a request is dynamically computed; it will be the locale
+name negotiated by the currently active :term:`locale negotiator` or
+the :term:`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 :ref:`default_locale_name_setting`.
-Once :func:`~pyramid.i18n.get_locale_name` is first run, the locale
+Once :func:`~pyramid.request.Request.locale_name` is first run, the locale
name is stored on the request object. Subsequent calls to
-:func:`~pyramid.i18n.get_locale_name` will return the stored locale
+:func:`~pyramid.request.Request.locale_name` will return the stored locale
name without invoking the :term:`locale negotiator`. To avoid this
caching, you can use the :func:`pyramid.i18n.negotiate_locale_name`
function:
@@ -650,15 +674,13 @@ You can also obtain the locale name related to a request using the
.. code-block:: python
:linenos:
- from pyramid.i18n import get_localizer
-
def aview(request):
- localizer = get_localizer(request)
+ localizer = request.localizer
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
-:func:`~pyramid.i18n.get_locale_name` function.
+to obtaining a locale name by asking for the
+:func:`~pyramid.request.Request.locale_name` attribute.
.. index::
single: date and currency formatting (i18n)
@@ -686,10 +708,9 @@ obtain the locale name for a request to pass to the
:linenos:
from babel.core import Locale
- from pyramid.i18n import get_locale_name
def aview(request):
- locale_name = get_locale_name(request)
+ locale_name = request.locale_name
locale = Locale(locale_name)
.. index::
@@ -808,7 +829,7 @@ If this setting is supplied within the :app:`Pyramid` application
default_locale_name = settings['pyramid.default_locale_name']
.. index::
- single: detecting langauges
+ single: detecting languages
"Detecting" Available Languages
-------------------------------
@@ -971,8 +992,8 @@ a particular request. A locale negotiator is a bit of code which
accepts a request and which returns a :term:`locale name`. It is
consulted when :meth:`pyramid.i18n.Localizer.translate` or
:meth:`pyramid.i18n.Localizer.pluralize` is invoked. It is also
-consulted when :func:`~pyramid.i18n.get_locale_name` or
-:func:`~pyramid.i18n.negotiate_locale_name` is invoked.
+consulted when :func:`~pyramid.request.Request.locale_name` is accessed or
+when :func:`~pyramid.i18n.negotiate_locale_name` is invoked.
.. _default_locale_negotiator:
@@ -984,7 +1005,7 @@ requires no additional coding or configuration.
The default locale negotiator implementation named
:class:`~pyramid.i18n.default_locale_negotiator` uses the following
-set of steps to dermine the locale name.
+set of steps to determine the locale name.
- First, the negotiator looks for the ``_LOCALE_`` attribute of the
request object (possibly set directly by view code or by a listener
diff --git a/docs/narr/install.rst b/docs/narr/install.rst
index 8fc63f3a4..e419a8b20 100644
--- a/docs/narr/install.rst
+++ b/docs/narr/install.rst
@@ -9,49 +9,54 @@ Installing :app:`Pyramid`
Before You Install
------------------
-You will need `Python <http://python.org>`_ version 2.6 or better to
-run :app:`Pyramid`.
+You will need `Python <http://python.org>`_ version 2.6 or better to run
+:app:`Pyramid`.
.. sidebar:: Python Versions
- As of this writing, :app:`Pyramid` has been tested under Python 2.6,
- Python 2.7, Python 3.2, and Python 3.3. :app:`Pyramid` does not
- run under any version of Python before 2.6.
+ As of this writing, :app:`Pyramid` has been tested under Python 2.6, Python
+ 2.7, Python 3.2, and Python 3.3. :app:`Pyramid` does not run under any
+ version of Python before 2.6.
-:app:`Pyramid` is known to run on all popular UNIX-like systems such as
-Linux, Mac OS X, and FreeBSD as well as on Windows platforms. It is
-also known to run on :term:`PyPy` (1.9+).
+:app:`Pyramid` is known to run on all popular UNIX-like systems such as Linux,
+Mac OS X, and FreeBSD as well as on Windows platforms. It is also known to run
+on :term:`PyPy` (1.9+).
-:app:`Pyramid` installation does not require the compilation of any
-C code, so you need only a Python interpreter that meets the
-requirements mentioned.
+:app:`Pyramid` installation does not require the compilation of any C code, so
+you need only a Python interpreter that meets the requirements mentioned.
+
+Some :app:`Pyramid` dependencies may attempt to build C extensions for
+performance speedups. If a compiler or Python headers are unavailable the
+dependency will fall back to using pure Python instead.
For Mac OS X Users
~~~~~~~~~~~~~~~~~~
From `Python.org <http://python.org/download/mac/>`_:
- Python comes pre-installed on Mac OS X, but due to Apple's release
- cycle, it's often one or even two years old. The overwhelming
- recommendation of the "MacPython" community is to upgrade your
- Python by downloading and installing a newer version from
- `the Python standard release page <http://python.org/download/releases/>`_.
+ Python comes pre-installed on Mac OS X, but due to Apple's release cycle,
+ it's often one or even two years old. The overwhelming recommendation of
+ the "MacPython" community is to upgrade your Python by downloading and
+ installing a newer version from `the Python standard release page
+ <http://python.org/download/releases/>`_.
-It is recommended to download one of the *installer* versions, unless you prefer to install your Python through a packgage manager (e.g., macports or homebrew) or to build your Python from source.
+It is recommended to download one of the *installer* versions, unless you
+prefer to install your Python through a packgage manager (e.g., macports or
+homebrew) or to build your Python from source.
-Unless you have a need for a specific earlier version, it is recommended
-to install the latest 2.x or 3.x version of Python.
+Unless you have a need for a specific earlier version, it is recommended to
+install the latest 2.x or 3.x version of Python.
-If you use an installer for your Python, then you can skip to the
-section :ref:`installing_unix`.
+If you use an installer for your Python, then you can skip to the section
+:ref:`installing_unix`.
If You Don't Yet Have A Python Interpreter (UNIX)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If your system doesn't have a Python interpreter, and you're on UNIX,
-you can either install Python using your operating system's package
-manager *or* you can install Python from source fairly easily on any
-UNIX system that has development tools.
+If your system doesn't have a Python interpreter, and you're on UNIX, you can
+either install Python using your operating system's package manager *or* you
+can install Python from source fairly easily on any UNIX system that has
+development tools.
.. index::
pair: install; Python (from package, UNIX)
@@ -59,9 +64,8 @@ UNIX system that has development tools.
Package Manager Method
++++++++++++++++++++++
-You can use your system's "package manager" to install Python.
-Each package manager is slightly different, but the "flavor" of
-them is usually the same.
+You can use your system's "package manager" to install Python. Each package
+manager is slightly different, but the "flavor" of them is usually the same.
For example, on a Debian or Ubuntu system, use the following command:
@@ -82,28 +86,27 @@ invokable via ``python2.7`` from a shell prompt.
Source Compile Method
+++++++++++++++++++++
-It's useful to use a Python interpreter that *isn't* the "system"
-Python interpreter to develop your software. The authors of
-:app:`Pyramid` tend not to use the system Python for development
-purposes; always a self-compiled one. Compiling Python is usually
-easy, and often the "system" Python is compiled with options that
-aren't optimal for web development. For an explanation, see
+It's useful to use a Python interpreter that *isn't* the "system" Python
+interpreter to develop your software. The authors of :app:`Pyramid` tend not
+to use the system Python for development purposes; always a self-compiled one.
+Compiling Python is usually easy, and often the "system" Python is compiled
+with options that aren't optimal for web development. For an explanation, see
https://github.com/Pylons/pyramid/issues/747.
-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:
+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:
.. code-block:: text
$ sudo apt-get install build-essential
-On Mac OS X, installing `XCode
-<http://developer.apple.com/tools/xcode/>`_ has much the same effect.
+On Mac OS X, installing `XCode <http://developer.apple.com/tools/xcode/>`_ has
+much the same effect.
-Once you've 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:
+Once you've 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:
.. code-block:: text
@@ -117,9 +120,8 @@ using the following commands:
$ ./configure --prefix=$HOME/opt/Python-2.7.3
$ make && make install
-Once these steps are performed, the Python interpreter will be
-invokable via ``$HOME/opt/Python-2.7.3/bin/python`` from a shell
-prompt.
+Once these steps are performed, the Python interpreter will be invokable via
+``$HOME/opt/Python-2.7.3/bin/python`` from a shell prompt.
.. index::
pair: install; Python (from package, Windows)
@@ -127,24 +129,21 @@ prompt.
If You Don't Yet Have A Python Interpreter (Windows)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-If your Windows system doesn't have a Python interpreter, you'll need
-to install it by downloading a Python 2.7-series interpreter
-executable from `python.org's download section
-<http://python.org/download/>`_ (the files labeled "Windows
-Installer"). Once you've downloaded it, double click on the
-executable and accept the defaults during the installation process.
-You may also need to download and install the `Python for Windows
-extensions <http://sourceforge.net/projects/pywin32/files/>`_.
+If your Windows system doesn't have a Python interpreter, you'll need to
+install it by downloading a Python 2.7-series interpreter executable from
+`python.org's download section <http://python.org/download/>`_ (the files
+labeled "Windows Installer"). Once you've downloaded it, double click on the
+executable and accept the defaults during the installation process. You may
+also need to download and install the Python for Windows extensions.
.. warning::
- After you install Python on Windows, you may need to add the
- ``C:\Python27`` directory to your environment's ``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.
+ After you install Python on Windows, you may need to add the ``C:\Python27``
+ directory to your environment's ``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.
.. index::
single: installing on UNIX
@@ -154,27 +153,26 @@ extensions <http://sourceforge.net/projects/pywin32/files/>`_.
Installing :app:`Pyramid` on a UNIX System
---------------------------------------------
-It is best practice to install :app:`Pyramid` into a "virtual"
-Python environment in order to obtain isolation from any "system"
-packages you've got installed in your Python version. This can be
-done by using the :term:`virtualenv` package. Using a virtualenv will
-also prevent :app:`Pyramid` from globally installing versions of
-packages that are not compatible with your system Python.
+It is best practice to install :app:`Pyramid` into a "virtual" Python
+environment in order to obtain isolation from any "system" packages you've got
+installed in your Python version. This can be done by using the
+:term:`virtualenv` package. Using a virtualenv will also prevent
+:app:`Pyramid` from globally installing versions of packages that are not
+compatible with your system Python.
To set up a virtualenv in which to install :app:`Pyramid`, first ensure that
-:term:`setuptools` or :term:`distribute` is installed. To do so, invoke
-``import setuptools`` within the Python interpreter you'd like to run
-:app:`Pyramid` under.
+:term:`setuptools` is installed. To do so, invoke ``import setuptools`` within
+the Python interpreter you'd like to run :app:`Pyramid` under.
-The following command will not display anything if setuptools or distribute is
-already installed:
+The following command will not display anything if setuptools is already
+installed:
.. code-block:: text
$ python2.7 -c 'import setuptools'
-Running the same command will yield the following output if setuptools or
-distribute is not yet installed:
+Running the same command will yield the following output if setuptools is not
+yet installed:
.. code-block:: text
@@ -183,62 +181,35 @@ distribute is not yet installed:
ImportError: No module named setuptools
If ``import setuptools`` raises an :exc:`ImportError` as it does above, you
-will need to install setuptools or distribute manually.
+will need to install setuptools manually.
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 system's package manager. If
-you cannot do this, or if you're 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 you're using Python 2, you'll want to install ``setuptools``. If you're
-using Python 3, you'll want to install ``distribute``. Below we tell you how
-to do both.
+third-party packager such as Fink or MacPorts), you can usually install the
+setuptools package by using your system's package manager. If you cannot do
+this, or if you're using a self-installed version of Python, you will need to
+install setuptools "by hand". Installing setuptools "by hand" is always a
+reasonable thing to do, even if your package manager already has a pre-chewed
+version of setuptools for installation.
-Installing Setuptools On Python 2
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Installing Setuptools
+~~~~~~~~~~~~~~~~~~~~~
To install setuptools by hand under Python 2, first download `ez_setup.py
-<http://peak.telecommunity.com/dist/ez_setup.py>`_ then invoke it using the
-Python interpreter into which you want to install setuptools.
+<https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py>`_ then invoke
+it using the Python interpreter into which you want to install setuptools.
.. code-block:: text
$ python ez_setup.py
-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:
-
-.. code-block:: text
-
- $ sudo python ez_setup.py
-
-Installing Distribute On Python 3
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-``setuptools`` doesn't work under Python 3. Instead, you can use
-``distribute``, which is a fork of setuptools. To
-install it, first download `distribute_setup.py
-<http://python-distribute.org/distribute_setup.py>`_ then invoke it using the
-Python interpreter into which you want to install setuptools.
-
-.. code-block:: text
-
- $ python3 distribute_setup.py
-
-Once this command is invoked, distribute should be installed on your 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:
.. code-block:: text
- $ sudo python3 distribute_setup.py
+ $ sudo python ez_setup.py
.. index::
pair: install; virtualenv
@@ -246,9 +217,9 @@ remediate this, you may need to do:
Installing the ``virtualenv`` Package
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Once you've got setuptools or distribute installed, you should install the
-:term:`virtualenv` package. To install the :term:`virtualenv` package into
-your setuptools-enabled Python interpreter, use the ``easy_install`` command.
+Once you've got setuptools installed, you should install the :term:`virtualenv`
+package. To install the :term:`virtualenv` package into your
+setuptools-enabled Python interpreter, use the ``easy_install`` command.
.. warning::
@@ -261,16 +232,16 @@ your setuptools-enabled Python interpreter, use the ``easy_install`` command.
Turing-complete.
If you insist on using ``pyvenv``, you'll need to understand how to install
- software such as ``distribute`` into the virtual environment manually,
- which this guide does not cover.
+ software such as ``setuptools`` into the virtual environment manually, which
+ this guide does not cover.
.. code-block:: text
$ 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 system's administrative user. For example:
+installed. If it fails due to permission errors, you may need to install it as
+your system's administrative user. For example:
.. code-block:: text
@@ -289,44 +260,49 @@ you can then create a virtual environment. To do so, invoke the following:
.. code-block:: text
$ export VENV=~/env
- $ virtualenv --no-site-packages $VENV
+ $ virtualenv $VENV
New python executable in /home/foo/env/bin/python
Installing setuptools.............done.
-You can either follow the use of the environment variable, ``$VENV``,
-or replace it with the root directory of the :term:`virtualenv`.
-In that case, the `export` command can be skipped.
-If you choose the former approach, ensure that it's an absolute path.
+You can either follow the use of the environment variable, ``$VENV``, or
+replace it with the root directory of the :term:`virtualenv`. In that case, the
+`export` command can be skipped. If you choose the former approach, ensure that
+it's an absolute path.
.. warning::
- Using ``--no-site-packages`` when generating your
- virtualenv is *very important*. This flag provides the necessary
- isolation for running the set of packages required by
- :app:`Pyramid`. If you do not specify ``--no-site-packages``,
- it's possible that :app:`Pyramid` will not install properly into
- the virtualenv, or, even if it does, may not run properly,
- depending on the packages you've already got installed into your
- Python's "main" site-packages dir.
+ Avoid using the ``--system-site-packages`` option when creating the
+ virtualenv unless you know what you are doing. For versions of virtualenv
+ prior to 1.7, make sure to use the ``--no-site-packages`` option, because
+ this option was formerly not the default and may produce undesirable
+ results.
-.. warning:: *do not* use ``sudo`` to run the
- ``virtualenv`` script. It's perfectly acceptable (and desirable)
- to create a virtualenv as a normal user.
+.. warning::
+
+ *do not* use ``sudo`` to run the ``virtualenv`` script. It's perfectly
+ acceptable (and desirable) to create a virtualenv as a normal user.
Installing :app:`Pyramid` Into the Virtual Python Environment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-After you've got your virtualenv installed, you may install
-:app:`Pyramid` itself using the following commands:
-
-.. code-block:: text
+After you've got your virtualenv installed, you may install :app:`Pyramid`
+itself using the following commands:
- $ $VENV/bin/easy_install pyramid
+.. parsed-literal::
+
+ $ $VENV/bin/easy_install "pyramid==\ |release|\ "
The ``easy_install`` command will take longer than the previous ones to
complete, as it downloads and installs a number of dependencies.
+.. note::
+
+ If you see any warnings and/or errors related to failing to compile the C
+ extensions, in most cases you may safely ignore those errors. If you wish
+ to use the C extensions, please verify that you have a functioning compiler
+ and the Python header files installed.
+
.. index::
single: installing on Windows
@@ -335,91 +311,39 @@ complete, as it downloads and installs a number of dependencies.
Installing :app:`Pyramid` on a Windows System
-------------------------------------------------
-You can use Pyramid on Windows under Python 2 or under Python 3. Directions
-for both versions are included below.
-
-Windows Using Python 2
-~~~~~~~~~~~~~~~~~~~~~~
+You can use Pyramid on Windows under Python 2 or 3.
-#. Install the most recent `Python 2.7.x version
+#. Download and install the most recent `Python 2.7.x or 3.3.x version
<http://www.python.org/download/>`_ for your system.
-#. Install the `Python for Windows extensions
- <http://sourceforge.net/projects/pywin32/files/>`_. Make sure to
- pick the right download for Python 2.7 and install it using the
- same Python installation from the previous step.
+#. Download and install the `Python for Windows extensions
+ <http://sourceforge.net/projects/pywin32/files/pywin32/>`_. Carefully read
+ the README.txt file at the end of the list of builds, and follow its
+ directions. Make sure you get the proper 32- or 64-bit build and Python
+ version.
-#. Install latest :term:`setuptools` distribution into the Python you
- obtained/installed/found in the step above: download `ez_setup.py
- <http://peak.telecommunity.com/dist/ez_setup.py>`_ and run it using
- the ``python`` interpreter of your Python 2.7 installation using a
- command prompt:
+#. Install latest :term:`setuptools` distribution into the Python from step 1
+ above: download `ez_setup.py
+ <https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py>`_ and run
+ it using the ``python`` interpreter of your Python 2.7 or 3.3 installation
+ using a command prompt:
.. code-block:: text
+ # modify the command according to the python version, e.g.:
+ # for Python 2.7:
c:\> c:\Python27\python ez_setup.py
+ # for Python 3.3:
+ c:\> c:\Python33\python ez_setup.py
#. Install `virtualenv`:
.. code-block:: text
- c:\> c:\Python27\Scripts\easy_install virtualenv
-
-#. Make a :term:`virtualenv` workspace:
-
- .. code-block:: text
-
- c:\> set VENV=c:\env
- c:\> c:\Python27\Scripts\virtualenv --no-site-packages %VENV%
-
- You can either follow the use of the environment variable, ``%VENV%``,
- or replace it with the root directory of the :term:`virtualenv`.
- In that case, the `set` command can be skipped.
- If you choose the former approach, ensure that it's an absolute path.
-
-#. (Optional) Consider using ``%VENV%\Scripts\activate.bat`` to make your shell
- environment wired to use the virtualenv.
-
-#. Use ``easy_install`` to get :app:`Pyramid` and its direct dependencies
- installed:
-
- .. code-block:: text
-
- c:\env> %VENV%\Scripts\easy_install pyramid
-
-Windows Using Python 3
-~~~~~~~~~~~~~~~~~~~~~~
-
-#. Install, or find the latest version of `Python 3.x
- <http://www.python.org/download/>`_ for your system and which is
- supported by Pyramid.
-
-#. Install the `Python for Windows extensions
- <http://sourceforge.net/projects/pywin32/files/>`_. Make sure to
- pick the right download for Python 3.x and install it using the
- same Python installation from the previous step.
-
-#. Install latest :term:`distribute` distribution into the Python you
- obtained/installed/found in the step above: download `distribute_setup.py
- <http://python-distribute.org/distribute_setup.py>`_ and run it using the
- ``python`` interpreter of your Python 3.x installation using a command
- prompt:
-
- .. code-block:: text
-
# modify the command according to the python version, e.g.:
- # for Python 3.2.x:
- c:\> c:\Python32\python distribute_setup.py
- # for Python 3.3.x:
- c:\> c:\Python33\python distribute_setup.py
-
-#. Install :term:`virtualenv`:
-
- .. code-block:: text
-
- # for Python 3.2.x:
- c:\> c:\Python32\Scripts\easy_install virtualenv
- # for Python 3.3.x:
+ # for Python 2.7:
+ c:\> c:\Python27\Scripts\easy_install virtualenv
+ # for Python 3.3:
c:\> c:\Python33\Scripts\easy_install virtualenv
#. Make a :term:`virtualenv` workspace:
@@ -427,15 +351,16 @@ Windows Using Python 3
.. code-block:: text
c:\> set VENV=c:\env
- # for Python 3.2.x:
- c:\> c:\Python32\Scripts\virtualenv --no-site-packages %VENV%
- # for Python 3.3.x:
- c:\> c:\Python33\Scripts\virtualenv --no-site-packages %VENV%
+ # modify the command according to the python version, e.g.:
+ # for Python 2.7:
+ c:\> c:\Python27\Scripts\virtualenv %VENV%
+ # for Python 3.3:
+ c:\> c:\Python33\Scripts\virtualenv %VENV%
- You can either follow the use of the environment variable, ``%VENV%``,
- or replace it with the root directory of the :term:`virtualenv`.
- In that case, the `set` command can be skipped.
- If you choose the former approach, ensure that it's an absolute path.
+ You can either follow the use of the environment variable, ``%VENV%``, or
+ replace it with the root directory of the :term:`virtualenv`. In that case,
+ the `set` command can be skipped. If you choose the former approach, ensure
+ that it's an absolute path.
#. (Optional) Consider using ``%VENV%\Scripts\activate.bat`` to make your shell
environment wired to use the virtualenv.
@@ -443,9 +368,9 @@ Windows Using Python 3
#. Use ``easy_install`` to get :app:`Pyramid` and its direct dependencies
installed:
- .. code-block:: text
-
- c:\env> %VENV%\Scripts\easy_install pyramid
+ .. parsed-literal::
+
+ c:\\env> %VENV%\\Scripts\\easy_install "pyramid==\ |release|\ "
What Gets Installed
-------------------
diff --git a/docs/narr/introduction.rst b/docs/narr/introduction.rst
index 48164d323..ece720a97 100644
--- a/docs/narr/introduction.rst
+++ b/docs/narr/introduction.rst
@@ -176,8 +176,14 @@ static file server in production without changing any code.
Example: :ref:`static_assets_section`.
-Debug Toolbar
-~~~~~~~~~~~~~
+Fully Interactive Development
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When developing a Pyramid application, several interactive features are
+available. Pyramid can automatically utilize changed templates when rendering
+pages and automatically restart the application to incorporate changed python
+code. Plain old ``printf()`` calls used for debugging can display to a
+console.
Pyramid's debug toolbar comes activated when you use a Pyramid scaffold to
render a project. This toolbar overlays your application in the browser, and
@@ -217,6 +223,8 @@ that the Pyramid core doesn't. 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
+
Class-based and function-based views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -319,7 +327,14 @@ assertion instead that the view returns "the right stuff" in the dictionary
it returns. You can write "real" unit tests instead of functionally testing
all of your views.
-For example, instead of:
+.. index::
+ pair: renderer; explicitly calling
+ pair: view renderer; explictly calling
+
+.. _example_render_to_response_call:
+
+For example, instead of returning a ``Response`` object from a
+``render_to_response`` call:
.. code-block:: python
:linenos:
@@ -330,7 +345,7 @@ For example, instead of:
return render_to_response('myapp:templates/mytemplate.pt', {'a':1},
request=request)
-You can do this:
+You can return a Python dictionary:
.. code-block:: python
:linenos:
@@ -775,7 +790,7 @@ automate some of the tedium away:
for method in ('GET', 'POST', 'HEAD'):
view = getattr(module, 'xhr_%s_view' % method, None)
if view is not None:
- config.add_view(view, route_name='xhr_route', xhr=True,
+ config.add_view(view, route_name='xhr_route', xhr=True,
permission='view', request_method=method)
config = Configurator()
@@ -857,7 +872,7 @@ It's our goal that no Pyramid question go unanswered. Whether you ask a
question on IRC, on the Pylons-discuss maillist, or on StackOverflow, you're
likely to get a reasonably prompt response. We don't tolerate "support
trolls" or other people who seem to get their rocks off by berating fellow
-users in our various offical support channels. We try to keep it well-lit
+users in our various official support channels. We try to keep it well-lit
and new-user-friendly.
Example: Visit irc\://freenode.net#pyramid (the ``#pyramid`` channel on
diff --git a/docs/narr/introspector.rst b/docs/narr/introspector.rst
index dec22c5b1..3c0a6744f 100644
--- a/docs/narr/introspector.rst
+++ b/docs/narr/introspector.rst
@@ -232,18 +232,6 @@ introspectables in categories not described here.
The factory object (the resolved ``factory`` argument to
``add_renderer``).
-``renderer globals factory``
-
- There will be one and only one introspectable in the ``renderer globals
- factory`` category. It represents a call to
- :meth:`pyramid.config.Configurator.set_renderer_globals_factory`; it will
- have the following data.
-
- ``factory``
-
- The factory object (the resolved ``factory`` argument to
- ``set_renderer_globals_factory``).
-
``routes``
Each introspectable in the ``routes`` category represents a call to
diff --git a/docs/narr/project.rst b/docs/narr/project.rst
index 9d69a65a5..8b7c24725 100644
--- a/docs/narr/project.rst
+++ b/docs/narr/project.rst
@@ -49,9 +49,7 @@ The included scaffolds are these:
URL mapping via :term:`URL dispatch` and no persistence mechanism.
``zodb``
- URL mapping via :term:`traversal` and persistence via :term:`ZODB`. *Note
- that, as of this writing, this scaffold will not run under Python 3, only
- under Python 2.*
+ URL mapping via :term:`traversal` and persistence via :term:`ZODB`.
``alchemy``
URL mapping via :term:`URL dispatch` and persistence via
@@ -146,9 +144,9 @@ contains no space characters, so it's 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``.
-.. warning::
+.. warning::
- You’ll need to avoid using ``pcreate`` to create a project with the same
+ You'll 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
conflict with Python standard library packages. You should also avoid
@@ -195,10 +193,10 @@ Elided output from a run of this command on UNIX is shown below:
...
Finished processing dependencies for MyProject==0.0
-This will install a :term:`distribution` representing your project into the
-interpreter's library set so it can be found by ``import`` statements and by
-other console scripts such as ``pserve``, ``pshell``, ``proutes`` and
-``pviews``.
+This will install a :term:`distribution` representing your project
+into the virtual environment interpreter's library set so it can be
+found by ``import`` statements and by other console scripts such as
+``pserve``, ``pshell``, ``proutes`` and ``pviews``.
.. index::
single: running tests
@@ -245,16 +243,16 @@ Here's sample output from a test run on UNIX:
OK
-.. note::
-
- The ``-q`` option is passed to the ``setup.py test`` command to limit the
- output to a stream of dots. If you don't pass ``-q``, you'll see more
- verbose test result output (which normally isn't 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.
+.. note::
+
+ The ``-q`` option is passed to the ``setup.py test`` command to limit the
+ output to a stream of dots. If you don't pass ``-q``, you'll see more
+ verbose test result output (which normally isn't very useful).
+
.. index::
single: running an application
single: pserve
@@ -572,8 +570,8 @@ adding more settings to this section.
The ``pyramid.reload_templates`` setting in the ``[app:main]`` section is a
:app:`Pyramid` -specific setting which is passed into the framework. If it
-exists, and its value is ``true``, :term:`Chameleon` and :term:`Mako`
-template changes will not require an application restart to be detected. See
+exists, and its value is ``true``, supported template changes will not
+require an application restart to be detected. See
:ref:`reload_templates_section` for more information.
.. warning:: The ``pyramid.reload_templates`` option should be turned off for
@@ -698,11 +696,11 @@ testing, packaging, and distributing your application.
.. note::
- ``setup.py`` is the de facto standard which Python developers use to
- distribute their reusable code. You can read more about ``setup.py`` files
- and their usage in the `Setuptools documentation
- <http://peak.telecommunity.com/DevCenter/setuptools>`_ and `The
- Hitchhiker's Guide to Packaging <http://guide.python-distribute.org/>`_.
+ ``setup.py`` is the de facto standard which Python developers use to
+ distribute their reusable code. You can read more about ``setup.py`` files
+ and their usage in the `Setuptools documentation
+ <http://peak.telecommunity.com/DevCenter/setuptools>`_ and `The
+ Hitchhiker's Guide to Packaging <http://guide.python-distribute.org/>`_.
Our generated ``setup.py`` looks like this:
@@ -820,7 +818,7 @@ also informs Python that the directory which contains it is a *package*.
#. Line 1 imports the :term:`Configurator` class from :mod:`pyramid.config`
that we use later.
-#. Lines 4-11 define a function named ``main`` that returns a :app:`Pyramid`
+#. Lines 4-12 define a function named ``main`` that returns a :app:`Pyramid`
WSGI application. This function is meant to be called by the
:term:`PasteDeploy` framework as a result of running ``pserve``.
@@ -828,17 +826,20 @@ also informs Python that the directory which contains it is a *package*.
Line 7 creates an instance of a :term:`Configurator`.
- Line 8 registers a static view, which will serve up the files from the
+ Line 8 adds support for Chameleon templating bindings, allowing us to
+ specify renderers with the ``.pt`` extension.
+
+ Line 9 registers a static view, which will serve up the files from the
``myproject:static`` :term:`asset specification` (the ``static``
directory of the ``myproject`` package).
- Line 9 adds a :term:`route` to the configuration. This route is later
+ Line 10 adds a :term:`route` to the configuration. This route is later
used by a view in the ``views`` module.
- Line 10 calls ``config.scan()``, which picks up view registrations declared
+ Line 11 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 :term:`WSGI` application to the caller of the function
+ Line 12 returns a :term:`WSGI` application to the caller of the function
(Pyramid's pserve).
.. index::
@@ -870,31 +871,40 @@ specification` that specifies the ``mytemplate.pt`` file within the
``templates`` directory of the ``myproject`` package. The asset
specification could have also been specified as
``myproject:templates/mytemplate.pt``; the leading package name and colon is
-optional. The template file it actually points to is a :term:`Chameleon` ZPT
-template file.
+optional. The template file pointed to is a :term:`Chameleon` ZPT
+template file (``templates/my_template.pt``).
This view callable function is handed a single piece of information: the
:term:`request`. The *request* is an instance of the :term:`WebOb`
``Request`` class representing the browser's request to our server.
-This view returns a dictionary. When this view is invoked, a
-:term:`renderer` converts the dictionary returned by the view into HTML, and
-returns the result as the :term:`response`. This view is configured to
-invoke a renderer which uses a :term:`Chameleon` ZPT template
-(``templates/my_template.pt``).
-
-See :ref:`views_which_use_a_renderer` for more information about how views,
-renderers, and templates relate and cooperate.
-
-.. note:: Because our ``development.ini`` has a ``pyramid.reload_templates =
- true`` directive indicating that templates should be reloaded when
- they change, you won't 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 project's ``pyramid.reload_templates`` to ``false`` to increase
- the speed at which templates may be rendered.
+This view is configured to invoke a :term:`renderer` on a template. The
+dictionary the view returns (on line 6) provides the value the renderer
+substitutes into the template when generating HTML. The renderer then
+returns the HTML in a :term:`response`.
+
+.. note:: Dictionaries provide values to :term:`template`\s.
+
+.. note:: ``development.ini`` has a setting that controls how templates are
+ reloaded, ``pyramid.reload_templates``.
+
+ - When set to ``True`` (as in the scaffold ``development.ini``) changed
+ templates automatically reload without a server restart. This is
+ convenient while developing, but slows template rendering speed.
+
+ - When set to ``False`` (the default value), changing templates requires
+ a server restart to reload them. Production applications should use
+ ``pyramid.reload_templates = False``.
+
+.. seealso:: See also :ref:`views_which_use_a_renderer` for more information
+ about how views, renderers, and templates relate and cooperate.
+
+.. seealso:: Pyramid can also dynamically reload changed Python files. For
+ more on this see :ref:`reloading_code`.
+
+.. seealso:: The :ref:`debug_toolbar` provides interactive access to your
+ application's internals and, should an exception occur, allows interactive
+ access to traceback execution stack frames from the Python interpreter.
.. index::
single: static directory
@@ -970,12 +980,15 @@ named ``views`` instead of within a single ``views.py`` file, you might:
- Create a ``views`` directory inside your ``myproject`` package directory
(the same directory which holds ``views.py``).
-- *Move* the existing ``views.py`` file to a file inside the new ``views``
- directory named, say, ``blog.py``.
+- Create a file within the new ``views`` directory named ``__init__.py``. (It
+ can be empty. This just tells Python that the ``views`` directory is a
+ *package*.)
-- Create a file within the new ``views`` directory named ``__init__.py`` (it
- can be empty, this just tells Python that the ``views`` directory is a
- *package*.
+- *Move* the content from the existing ``views.py`` file to a file inside the
+ new ``views`` directory named, say, ``blog.py``. Because the ``templates``
+ directory remains in the ``myproject`` package, the template :term:`asset
+ specification` values in ``blog.py`` must now be fully qualified with the
+ project's package name (``myproject:templates/blog.pt``).
You can then continue to add view callable functions to the ``blog.py``
module, but you can also add other ``.py`` files which contain view callable
@@ -992,6 +1005,8 @@ prompt with a similar configuration as would be loaded if you were running
your Pyramid application via ``pserve``. This can be a useful debugging tool.
See :ref:`interactive_shell` for more details.
+.. _what_is_this_pserve_thing:
+
What Is This ``pserve`` Thing
-----------------------------
@@ -1005,12 +1020,12 @@ Pyramid application based on the data in the file.
application. As we saw in :ref:`firstapp_chapter`, ``pserve`` needn't be
invoked at all to run a :app:`Pyramid` application. The use of ``pserve`` to
run a :app:`Pyramid` application is purely conventional based on the output
-of its scaffolding. But we strongly recommend using while developing your
-application, because many other convenience introspection commands (such as
-``pviews``, ``prequest``, ``proutes`` and others) are also implemented in
-terms of configuration availability of this ``.ini`` file format. It also
-configures Pyramid logging and provides the ``--reload`` switch for
-convenient restarting of the server when code changes.
+of its scaffolding. But we strongly recommend using ``pserve`` while
+developing your application, because many other convenience introspection
+commands (such as ``pviews``, ``prequest``, ``proutes`` and others) are also
+implemented in terms of configuration availability of this ``.ini`` file
+format. It also configures Pyramid logging and provides the ``--reload``
+switch for convenient restarting of the server when code changes.
.. _alternate_wsgi_server:
@@ -1022,7 +1037,7 @@ server. Waitress is a server that is suited for development and light
production usage. It's not the fastest nor the most featureful WSGI server.
Instead, its main feature is that it works on all platforms that Pyramid
needs to run on, making it a good choice as a default server from the
-perspective of Pyramid's developers.
+perspective of Pyramid's developers.
Any WSGI server is capable of running a :app:`Pyramid` application. But we
suggest you stick with the default server for development, and that you wait
diff --git a/docs/narr/renderers.rst b/docs/narr/renderers.rst
index b4eb95186..740c81555 100644
--- a/docs/narr/renderers.rst
+++ b/docs/narr/renderers.rst
@@ -33,14 +33,11 @@ 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 configured. If a view callable returns a response
-object from a view that is configured with a renderer, the renderer is
+when a ``renderer`` is configured. In such cases, the renderer is
bypassed entirely.
Various types of renderers exist, including serialization renderers
-and renderers which use templating systems. See also
-:ref:`views_which_use_a_renderer`.
-
+and renderers which use templating systems.
.. index::
single: renderer
@@ -51,16 +48,20 @@ and renderers which use templating systems. See also
Writing View Callables Which Use a Renderer
-------------------------------------------
-As we've seen, view callables needn't always return a Response object.
-Instead, they may return an arbitrary Python object, with the expectation
-that a :term:`renderer` will convert that object into a response instance on
-your behalf. Some renderers use a templating system; other renderers use
-object serialization techniques.
-
-View configuration can vary the renderer associated with a view callable via
-the ``renderer`` attribute. For example, this call to
-:meth:`~pyramid.config.Configurator.add_view` associates the ``json`` renderer
-with a view callable:
+As we've seen, a view callable needn't always return a Response object.
+Instead, it may return an arbitrary Python object, with the expectation that
+a :term:`renderer` will convert that object into a response instance on your
+behalf. Some renderers use a templating system; other renderers use object
+serialization techniques. In practice, renderers obtain application data
+values from Python dictionaries so, in practice, view callables which use
+renderers return Python dictionaries.
+
+View callables can :ref:`explicitly call <example_render_to_response_call>`
+renderers, but typically don't. Instead view configuration declares the
+renderer used to render a view callable's results. This is done with the
+``renderer`` attribute. For example, this call to
+:meth:`~pyramid.config.Configurator.add_view` associates the ``json``
+renderer with a view callable:
.. code-block:: python
@@ -70,9 +71,8 @@ When this configuration 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 :term:`JSON` response serialization.
-Other built-in renderers include renderers which use the :term:`Chameleon`
-templating language to render a dictionary to a response. Additional
-renderers can be added by developers to the system as necessary.
+Pyramid defines several :ref:`built_in_renderers`, and additional renderers
+can be added by developers to the system as necessary.
See :ref:`adding_and_overriding_renderers`.
Views which use a renderer and return a non-Response value can vary non-body
@@ -80,12 +80,10 @@ response attributes (such as headers and the HTTP status code) by attaching a
property to the ``request.response`` attribute.
See :ref:`request_response_attr`.
-If the :term:`view callable` associated with a :term:`view configuration`
-returns a Response object directly, any renderer associated with the view
-configuration is ignored, and the response is passed back to :app:`Pyramid`
-unchanged. For example, if your view callable returns an instance of the
-:class:`pyramid.response.Response` class as a response, no renderer
-will be employed.
+As already mentioned, if the :term:`view callable` associated with a
+:term:`view configuration` returns a Response object (or its instance),
+any renderer associated with the view configuration is ignored,
+and the response is passed back to :app:`Pyramid` unchanged. For example:
.. code-block:: python
:linenos:
@@ -134,13 +132,18 @@ Built-In Renderers
Several built-in renderers exist in :app:`Pyramid`. These renderers can be
used in the ``renderer`` attribute of view configurations.
+.. note::
+
+ Bindings for officially supported templating languages can be found
+ at :ref:`available_template_system_bindings`.
+
.. index::
pair: renderer; string
``string``: String Renderer
~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The ``string`` renderer is a renderer which renders a view callable result to
+The ``string`` renderer renders a view callable result to
a string. If a view callable returns a non-Response object, and the
``string`` renderer is associated in that view's configuration, the result
will be to run the object through the Python ``str`` function to generate a
@@ -203,18 +206,12 @@ representing the JSON serialization of the return value:
.. code-block:: python
- '{"content": "Hello!"}'
+ {"content": "Hello!"}
The return value needn't be a dictionary, but the return value must contain
values serializable by the configured serializer (by default ``json.dumps``).
-.. note::
-
- Extra arguments can be passed to the serializer by overriding the default
- ``json`` renderer. See :class:`pyramid.renderers.JSON` and
- :ref:`adding_and_overriding_renderers` for more information.
-
-You can configure a view to use the JSON renderer by naming ``json`` as the
+You can configure a view to use the JSON renderer by naming``json`` as the
``renderer`` argument of a view configuration, e.g. by using
:meth:`~pyramid.config.Configurator.add_view`:
@@ -235,6 +232,18 @@ using the api of the ``request.response`` attribute. See
Serializing Custom Objects
++++++++++++++++++++++++++
+Some objects are not, by default, JSON-serializable (such as datetimes and
+other arbitrary Python objects). You can, however, register code that makes
+non-serializable objects serializable in two ways:
+
+- By defining a ``__json__`` method on objects in your application.
+
+- For objects you don't "own", you can register JSON renderer that knows about
+ an *adapter* for that kind of object.
+
+Using a Custom ``__json__`` Method
+**********************************
+
Custom objects can be made easily JSON-serializable in Pyramid by defining a
``__json__`` method on the object's class. This method should return values
natively JSON-serializable (such as ints, lists, dictionaries, strings, and
@@ -260,6 +269,9 @@ will be the active request object at render time.
# the JSON value returned by ``objects`` will be:
# [{"x": 1}, {"x": 2}]
+Using the ``add_adapter`` Method of a Custom JSON Renderer
+**********************************************************
+
If you aren't the author of the objects being serialized, it won't be
possible (or at least not reasonable) to add a custom ``__json__`` method
to their classes in order to influence serialization. If the object passed
@@ -274,19 +286,21 @@ objects using the registered adapters. A short example follows:
from pyramid.renderers import JSON
- json_renderer = JSON()
- def datetime_adapter(obj, request):
- return obj.isoformat()
- json_renderer.add_adapter(datetime.datetime, datetime_adapter)
+ if __name__ == '__main__':
+ config = Configurator()
+ json_renderer = JSON()
+ def datetime_adapter(obj, request):
+ return obj.isoformat()
+ json_renderer.add_adapter(datetime.datetime, datetime_adapter)
+ config.add_renderer('json', json_renderer)
- # then during configuration ....
- config = Configurator()
- config.add_renderer('json', json_renderer)
+The ``add_adapter`` method should accept two arguments: the *class* of the object that you want this adapter to run for (in the example above,
+``datetime.datetime``), and the adapter itself.
-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 :exc:`TypeError` if it can't determine what to do
-with the object.
+The adapter should be a callable. It 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 :exc:`TypeError`
+if it can't determine what to do with the object.
See :class:`pyramid.renderers.JSON` and
:ref:`adding_and_overriding_renderers` for more information.
@@ -371,136 +385,6 @@ renderer in :ref:`json_serializing_custom_objects` can be used when passing
values to a JSONP renderer too.
.. index::
- pair: renderer; chameleon
-
-.. _chameleon_template_renderers:
-
-``*.pt`` or ``*.txt``: Chameleon Template Renderers
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Two built-in renderers exist for :term:`Chameleon` templates.
-
-If the ``renderer`` attribute of a view configuration is an absolute path, a
-relative path or :term:`asset specification` which has a final path element
-with a filename extension of ``.pt``, the Chameleon ZPT renderer is used.
-See :ref:`chameleon_zpt_templates` for more information about ZPT templates.
-
-If the ``renderer`` attribute of a view configuration is an absolute path or
-a :term:`asset specification` which has a final path element with a filename
-extension of ``.txt``, the :term:`Chameleon` text renderer is used. See
-:ref:`chameleon_text_templates` for more information about Chameleon text
-templates.
-
-The behavior of these renderers is the same, except for the engine
-used to render the template.
-
-When a ``renderer`` attribute that names a template path or :term:`asset
-specification` (e.g. ``myproject:templates/foo.pt`` or
-``myproject:templates/foo.txt``) is used, the view must return a
-:term:`Response` object or a Python *dictionary*. If the view callable with
-an associated template returns a Python dictionary, the named template will
-be passed the dictionary as its keyword arguments, and the template renderer
-implementation will return the resulting rendered template in a response to
-the user. If the view callable returns anything but a Response object or a
-dictionary, an error will be raised.
-
-Before passing keywords to the template, the keyword arguments derived from
-the dictionary returned by the view are augmented. The callable object --
-whatever object was used to define the view -- will be 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+.
-
-Here's an example view configuration which uses a Chameleon ZPT renderer:
-
-.. code-block:: python
- :linenos:
-
- # config is an instance of pyramid.config.Configurator
-
- config.add_view('myproject.views.hello_world',
- name='hello',
- context='myproject.resources.Hello',
- renderer='myproject:templates/foo.pt')
-
-Here's an example view configuration which uses a Chameleon text renderer:
-
-.. code-block:: python
- :linenos:
-
- config.add_view('myproject.views.hello_world',
- name='hello',
- context='myproject.resources.Hello',
- renderer='myproject:templates/foo.txt')
-
-Views which use a Chameleon renderer can vary response attributes by using
-the API of the ``request.response`` attribute. See
-:ref:`request_response_attr`.
-
-.. index::
- pair: renderer; mako
-
-.. _mako_template_renderers:
-
-``*.mak`` or ``*.mako``: Mako Template Renderer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-The ``Mako`` template renderer renders views using a Mako template. When
-used, the view must return a Response object or a Python *dictionary*. The
-dictionary items will then be used in the global template space. If the view
-callable returns anything but a Response object or a dictionary, an error
-will be raised.
-
-When using a ``renderer`` argument to a :term:`view configuration` to specify
-a Mako template, the value of the ``renderer`` may be a path relative to the
-``mako.directories`` setting (e.g. ``some/template.mak``) or, alternately,
-it may be a :term:`asset specification`
-(e.g. ``apackage:templates/sometemplate.mak``). Mako templates may
-internally inherit other Mako templates using a relative filename or a
-:term:`asset specification` as desired.
-
-Here's an example view configuration which uses a relative path:
-
-.. code-block:: python
- :linenos:
-
- # config is an instance of pyramid.config.Configurator
-
- config.add_view('myproject.views.hello_world',
- name='hello',
- context='myproject.resources.Hello',
- renderer='foo.mak')
-
-It's important to note that in Mako's case, the 'relative' path name
-``foo.mak`` above is not relative to the package, but is relative to the
-directory (or directories) configured for Mako via the ``mako.directories``
-configuration file setting.
-
-The renderer can also be provided in :term:`asset specification`
-format. Here's an example view configuration which uses one:
-
-.. code-block:: python
- :linenos:
-
- config.add_view('myproject.views.hello_world',
- name='hello',
- context='myproject.resources.Hello',
- renderer='mypackage:templates/foo.mak')
-
-The above configuration will use the file 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
-:ref:`environment_chapter` for additional
-:ref:`mako_template_renderer_settings`.
-
-.. index::
single: response headers (from a renderer)
single: renderer response headers
@@ -561,40 +445,6 @@ For more information on attributes of the request, see the API documentation
in :ref:`request_module`. For more information on the API of
``request.response``, see :attr:`pyramid.request.Request.response`.
-.. _response_prefixed_attrs:
-
-Deprecated Mechanism to Vary Attributes of Rendered Responses
--------------------------------------------------------------
-
-In previous releases of Pyramid (1.0 and before), the ``request.response``
-attribute did not exist. Instead, Pyramid required users to set special
-``response_`` -prefixed attributes of the request to influence response
-behavior. As of Pyramid 1.1, those request attributes are deprecated and
-their use will cause a deprecation warning to be issued when used. Until
-their existence is removed completely, we document them below, for benefit of
-people with older code bases.
-
-``response_content_type``
- Defines 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 influence ``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.
-
.. _adding_and_overriding_renderers:
Adding and Changing Renderers
@@ -744,44 +594,37 @@ 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 configuration.
-.. index::
- pair: renderer; changing
+Adding a Default Renderer
+~~~~~~~~~~~~~~~~~~~~~~~~~
-Changing an Existing Renderer
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-You can associate more than one filename extension with the same existing
-renderer implementation as necessary if you need to use a different file
-extension for the same kinds of templates. For example, to associate the
-``.zpt`` extension with the Chameleon ZPT renderer factory, use the
-:meth:`pyramid.config.Configurator.add_renderer` method:
+To associate a *default* renderer with *all* view configurations (even
+ones which do not possess a ``renderer`` attribute), pass ``None`` as
+the ``name`` attribute to the renderer tag:
.. code-block:: python
- config.add_renderer('.zpt', 'pyramid.chameleon_zpt.renderer_factory')
-
-After you do this, :app:`Pyramid` will treat templates ending in both the
-``.pt`` and ``.zpt`` filename extensions as Chameleon ZPT templates.
-
-To change the default mapping in which files with a ``.pt`` extension are
-rendered via a Chameleon ZPT page template renderer, use a variation on the
-following in your application's startup code:
-
-.. code-block:: python
+ config.add_renderer(None, 'mypackage.json_renderer_factory')
- config.add_renderer('.pt', 'mypackage.pt_renderer')
+.. index::
+ pair: renderer; changing
-After you do this, the :term:`renderer factory` in
-``mypackage.pt_renderer`` will be used to render templates which end
-in ``.pt``, replacing the default Chameleon ZPT renderer.
+Changing an Existing Renderer
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To associate a *default* renderer with *all* view configurations (even
-ones which do not possess a ``renderer`` attribute), pass ``None`` as
-the ``name`` attribute to the renderer tag:
+Pyramid supports overriding almost every aspect of its setup through its
+:ref:`Conflict Resolution <automatic_conflict_resolution>` mechanism. This
+means that in most cases overriding a renderer is as simple as using the
+:meth:`pyramid.config.Configurator.add_renderer` method to re-define the
+template extension. For example, if you would like to override the ``.txt``
+extension to specify a new renderer you could do the following:
.. code-block:: python
- config.add_renderer(None, 'mypackage.json_renderer_factory')
+ json_renderer = pyramid.renderers.JSON()
+ config.add_renderer('json', json_renderer)
+
+After doing this, any views registered with the ``json`` renderer will use
+the new renderer.
.. index::
pair: renderer; overriding at runtime
diff --git a/docs/narr/resources.rst b/docs/narr/resources.rst
index 699a3d4ac..b1bb611e5 100644
--- a/docs/narr/resources.rst
+++ b/docs/narr/resources.rst
@@ -300,7 +300,7 @@ the resource by :meth:`~pyramid.request.Request.resource_url`.
The ``__resource_url__`` hook is passed two arguments: ``request`` and
``info``. ``request`` is the :term:`request` object passed to
:meth:`~pyramid.request.Request.resource_url`. ``info`` is a dictionary with
-two keys:
+the following keys:
``physical_path``
A string representing the "physical path" computed for the resource, as
diff --git a/docs/narr/sessions.rst b/docs/narr/sessions.rst
index c4f4b5f07..649d22bd2 100644
--- a/docs/narr/sessions.rst
+++ b/docs/narr/sessions.rst
@@ -99,6 +99,11 @@ example:
else:
return Response('Fred was not in the session')
+The first time this view is invoked produces ``Fred was not in the
+session``. Subsequent invocations produce ``Fred was in the
+session``, assuming of course that the client side maintains the
+session's identity across multiple requests.
+
You can use a session much like a Python dictionary. It supports all
dictionary methods, along with some extra attributes, and methods.
@@ -146,8 +151,7 @@ Some gotchas:
you've changed sessioning data.
.. index::
- single: pyramid_beaker
- single: Beaker
+ single: pyramid_redis_sessions
single: session factory (alternates)
.. _using_alternate_session_factories:
@@ -155,13 +159,10 @@ Some gotchas:
Using Alternate Session Factories
---------------------------------
-At the time of this writing, exactly one alternate session factory
-implementation exists, named ``pyramid_beaker``. This is a session factory
-that uses the `Beaker <http://beaker.groovie.org/>`_ library as a backend.
-Beaker has support for file-based sessions, database based sessions, and
-encrypted cookie-based sessions. See `the pyramid_beaker documentation
-<http://docs.pylonsproject.org/projects/pyramid_beaker/en/latest/>`_ for more
-information about ``pyramid_beaker``.
+At the time of this writing, exactly one project-endorsed alternate session
+factory exists named :term:`pyramid_redis_sessions`. It can be downloaded from
+PyPI. It uses the Redis database as a backend. It is the recommended
+persistent session solution at the time of this writing.
.. index::
single: session factory (custom)
@@ -181,6 +182,8 @@ implementation in the :mod:`pyramid.session` module as inspiration.
.. index::
single: flash messages
+.. _flash_messages:
+
Flash Messages
--------------
@@ -298,14 +301,15 @@ Preventing Cross-Site Request Forgery Attacks
`Cross-site request forgery
<http://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a
-phenomenon whereby a user with an identity on your website might click on a
-URL or button on another website which secretly redirects the user to your
-application to perform some command that requires elevated privileges.
-
-You can avoid most of these attacks by making sure that the correct *CSRF
-token* has been set in an :app:`Pyramid` session object before performing any
-actions in code which requires elevated privileges that is invoked via a form
-post. To use CSRF token support, you must enable a :term:`session factory`
+phenomenon whereby a user who is logged in to your website might inadvertantly
+load a URL because it is linked from, or embedded in, an attacker's website.
+If the URL is one that may modify or delete data, the consequences can be dire.
+
+You can avoid most of these attacks by issuing a unique token to the browser
+and then requiring that it be present in all potentially unsafe requests.
+:app:`Pyramid` sessions provide facilities to create and check CSRF tokens.
+
+To use CSRF tokens, you must first enable a :term:`session factory`
as described in :ref:`using_the_default_session_factory` or
:ref:`using_alternate_session_factories`.
@@ -324,33 +328,82 @@ To get the current CSRF token from the session, use the
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
+``session.new_csrf_token()`` was invoked previously for this session, then 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.
+this session, then 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 field 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 field. For example, if your form rendering
-included the CSRF token obtained via ``session.get_csrf_token()`` as a hidden
-input field named ``csrf_token``:
+posts to a method that requires elevated privileges, or supply it as a request
+header in AJAX requests.
+
+For example, include the CSRF token as a hidden field:
+
+.. code-block:: html
+
+ <form method="post" action="/myview">
+ <input type="hidden" name="csrf_token" value="${request.session.get_csrf_token()}">
+ <input type="submit" value="Delete Everything">
+ </form>
+
+Or, include it as a header in a jQuery AJAX request:
+
+.. code-block:: javascript
+
+ var csrfToken = ${request.session.get_csrf_token()};
+ $.ajax({
+ type: "POST",
+ url: "/myview",
+ headers: { 'X-CSRF-Token': csrfToken }
+ }).done(function() {
+ alert("Deleted");
+ });
+
+
+The handler for the URL that receives the request
+should then require that the correct CSRF token is supplied.
+
+Using the ``session.check_csrf_token`` Method
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In request handling code, you can check the presence and validity of a CSRF
+token with ``session.check_csrf_token(request)``. If the token is valid,
+it will return True, otherwise it will raise ``HTTPBadRequest``.
+
+By default, it checks for a GET or POST parameter named ``csrf_token`` or a
+header named ``X-CSRF-Token``.
.. code-block:: python
- :linenos:
- token = request.session.get_csrf_token()
- if token != request.POST['csrf_token']:
- raise ValueError('CSRF token did not match')
+ def myview(request):
+ session = request.session
+
+ # Require CSRF Token
+ session.check_csrf_token(request):
+
+ ...
.. index::
single: session.new_csrf_token
+Checking CSRF Tokens With A View Predicate
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+A convenient way to require a valid CSRF Token for a particular view is to
+include ``check_csrf=True`` as a view predicate.
+See :meth:`pyramid.config.Configurator.add_route`.
+
+.. code-block:: python
+
+ @view_config(request_method='POST', check_csrf=True, ...)
+ def myview(request):
+ ...
+
+
Using the ``session.new_csrf_token`` Method
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-To explicitly add a new CSRF token to the session, use the
+To explicitly create a new CSRF token, use the
``session.new_csrf_token()`` method. This differs only from
``session.get_csrf_token()`` inasmuch as it clears any existing CSRF token,
creates a new CSRF token, sets the token into the session, and returns the
diff --git a/docs/narr/subrequest.rst b/docs/narr/subrequest.rst
index 6437bd0fa..4b4e99d41 100644
--- a/docs/narr/subrequest.rst
+++ b/docs/narr/subrequest.rst
@@ -232,12 +232,12 @@ unconditionally:
- Ensures that the user implied by the request passed has the necessary
authorization to invoke view callable before calling it.
-- causes a :class:`~pyramid.events.NewResponse` event to be sent when the
- Pyramid application returns a response.
-
- Calls any :term:`response callback` functions defined within the subrequest's
lifetime if a response is obtained from the Pyramid application.
+- causes a :class:`~pyramid.events.NewResponse` event to be sent if a response
+ is obtained.
+
- Calls any :term:`finished callback` functions defined within the subrequest's
lifetime.
diff --git a/docs/narr/templates.rst b/docs/narr/templates.rst
index d4cf20b93..3e19f7198 100644
--- a/docs/narr/templates.rst
+++ b/docs/narr/templates.rst
@@ -8,10 +8,6 @@ dynamic data provided by a :term:`view`. :app:`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, :app:`Pyramid` provides templating via the :term:`Chameleon`
-and :term:`Mako` templating libraries. :term:`Chameleon` provides support for
-two different types of templates: :term:`ZPT` templates, and text templates.
-
Before discussing how built-in templates are used in
detail, we'll discuss two ways to render templates within
:app:`Pyramid` in general: directly, and via renderer
@@ -32,7 +28,7 @@ given templating engine to do so.
:app:`Pyramid` provides various APIs that allow you to render templates
directly from within a view callable. For example, if there is a
-:term:`Chameleon` ZPT template named ``foo.pt`` in a directory named
+:term:`Chameleon` ZPT template named ``foo.pt`` in a directory named
``templates`` in your application, you can render the template from
within the body of a view callable like so:
@@ -60,19 +56,8 @@ In this case, this is the directory containing the file that
defines 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
-prefix on Windows.
-
-.. warning::
-
- Only :term:`Chameleon` templates support defining a renderer for a
- template relative to the location of the module where the view callable is
- defined. Mako templates, and other templating system bindings work
- differently. In particular, Mako templates use a "lookup path" as defined
- by the ``mako.directories`` configuration file instead of treating
- relative paths as relative to the current view module. See
- :ref:`mako_templates`.
-
-The path can alternately be a :term:`asset specification` in the form
+prefix on Windows. The path can alternately be a
+:term:`asset specification` in the form
``some.dotted.package_name:relative/path``. This makes it possible to
address template assets which live in another package. For example:
@@ -90,16 +75,9 @@ An asset specification points at a file within a Python *package*.
In this case, it points at a file named ``foo.pt`` within the
``templates`` directory of the ``mypackage`` package. Using a
asset specification instead of a relative template name is usually
-a good idea, because calls to ``render_to_response`` using asset
-specifications will continue to work properly if you move the code
-containing them around.
-
-.. note::
-
- Mako templating system bindings also respect absolute asset
- specifications as an argument to any of the ``render*`` commands. If a
- template name defines a ``:`` (colon) character and is not an absolute
- path, it is treated as an absolute asset specification.
+a good idea, because calls to :func:`~pyramid.renderers.render_to_response`
+using asset specifications will continue to work properly if you move the
+code containing them around.
In the examples above we pass in a keyword argument named ``request``
representing the current :app:`Pyramid` request. Passing a request
@@ -147,8 +125,8 @@ import its API functions into your views module, use those APIs to generate a
string, then return that string as the body of a :app:`Pyramid`
:term:`Response` object.
-For example, here's an example of using "raw" `Mako
-<http://www.makotemplates.org/>`_ from within a :app:`Pyramid` :term:`view`:
+For example, here's an example of using "raw" Mako_ from within a
+:app:`Pyramid` :term:`view`:
.. code-block:: python
:linenos:
@@ -163,10 +141,10 @@ For example, here's an example of using "raw" `Mako
return response
You probably wouldn't use this particular snippet in a project, because it's
-easier to use the Mako renderer bindings which already exist in
-:app:`Pyramid`. But if your favorite templating system is not supported as a
-renderer extension for :app:`Pyramid`, you can create your own simple
-combination as shown above.
+easier to use the supported
+:ref:`Mako bindings <available_template_system_bindings>`. But if your
+favorite templating system is not supported as a renderer extension for
+:app:`Pyramid`, you can create your own simple combination as shown above.
.. note::
@@ -281,8 +259,8 @@ You can define more values which will be passed to every template executed as
a result of rendering by defining :term:`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.
+renderer itself, but most template renderers make these names available as
+top-level template variables.
.. index::
pair: renderer; templates
@@ -322,7 +300,9 @@ template renderer:
def my_view(request):
return {'foo':1, 'bar':2}
-.. note:: You do not need to supply the ``request`` value as a key
+.. note::
+
+ You do not need to supply the ``request`` value as a key
in the dictionary result returned from a renderer-configured view
callable. :app:`Pyramid` automatically supplies this value for
you so that the "most correct" system values are provided to
@@ -350,11 +330,7 @@ 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 specifically for :app:`Pyramid` to use a
-templating language template as a renderer. Currently, :app:`Pyramid` has
-built-in support for two Chameleon templating languages: ZPT and text, and
-the Mako templating system. See :ref:`built_in_renderers` for a discussion
-of their details. :app:`Pyramid` also supports the use of :term:`Jinja2`
-templates as renderers. See :ref:`available_template_system_bindings`.
+templating language template as a renderer.
.. sidebar:: Why Use A Renderer via View Configuration
@@ -383,239 +359,11 @@ The same set of system values are provided to templates rendered via a
renderer view configuration as those provided to templates rendered
imperatively. See :ref:`renderer_system_values`.
-
-.. index::
- single: Chameleon ZPT templates
- single: ZPT templates (Chameleon)
-
-.. _chameleon_zpt_templates:
-
-:term:`Chameleon` ZPT Templates
--------------------------------
-
-Like :term:`Zope`, :app:`Pyramid` uses :term:`ZPT` (Zope Page
-Templates) as its default templating language. However,
-:app:`Pyramid` uses a different implementation of the :term:`ZPT`
-specification than Zope does: the :term:`Chameleon` templating
-engine. The Chameleon engine complies largely with the `Zope Page
-Template <http://wiki.zope.org/ZPT/FrontPage>`_ template
-specification. However, it is significantly faster.
-
-The language definition documentation for Chameleon ZPT-style
-templates is available from `the Chameleon website
-<http://chameleon.repoze.org/>`_.
-
-Given a :term:`Chameleon` ZPT template named ``foo.pt`` in a directory
-in your application named ``templates``, you can render the template as
-a :term:`renderer` like so:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import view_config
-
- @view_config(renderer='templates/foo.pt')
- def my_view(request):
- return {'foo':1, 'bar':2}
-
-See also :ref:`built_in_renderers` for more general information about
-renderers, including Chameleon ZPT renderers.
-
-.. index::
- single: ZPT template (sample)
-
-A Sample ZPT Template
-~~~~~~~~~~~~~~~~~~~~~
-
-Here's what a simple :term:`Chameleon` ZPT template used under
-:app:`Pyramid` might look like:
-
-.. code-block:: xml
- :linenos:
-
- <!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 :term:`Genshi` -style ``${replacements}`` above. This
-is one of the ways that :term:`Chameleon` ZPT differs from standard
-ZPT. The above template expects to find a ``project`` key in the set
-of keywords passed in to it via :func:`~pyramid.renderers.render` or
-:func:`~pyramid.renderers.render_to_response`. Typical ZPT
-attribute-based syntax (e.g. ``tal:content`` and ``tal:replace``) also
-works in these templates.
-
-.. index::
- single: ZPT macros
- single: Chameleon ZPT macros
-
-Using ZPT Macros in :app:`Pyramid`
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-When a :term:`renderer` is used to render a template, :app:`Pyramid` makes at
-least two top-level names available to the template by default: ``context``
-and ``request``. One of the common needs in ZPT-based templates is to use
-one template's "macros" from within a different template. In Zope, this is
-typically handled by retrieving the template from the ``context``. But the
-context in :app:`Pyramid` is a :term:`resource` object, and templates cannot
-usually be retrieved from resources. To use macros in :app:`Pyramid`, you
-need to make the macro template itself available to the rendered template by
-passing the macro template, or even the macro itself, *into* the rendered
-template. To do this you can use the :func:`pyramid.renderers.get_renderer`
-API to retrieve the macro template, and pass it into the template being
-rendered via the dictionary returned by the view. For example, using a
-:term:`view configuration` via a :class:`~pyramid.view.view_config` decorator
-that uses a :term:`renderer`:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.renderers import get_renderer
- from pyramid.view import view_config
-
- @view_config(renderer='templates/mytemplate.pt')
- def my_view(request):
- main = get_renderer('templates/master.pt').implementation()
- return {'main':main}
-
-Where ``templates/master.pt`` might look like so:
-
-.. code-block:: xml
- :linenos:
-
- <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>
-
-And ``templates/mytemplate.pt`` might look like so:
-
-.. code-block:: xml
- :linenos:
-
- <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>
-
-
-Using A Chameleon Macro Name Within a Renderer Name
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-At times, you may want to render a macro inside of a Chameleon ZPT template
-instead of the full Chameleon ZPT template. To render the content of a
-``define-macro`` field inside a Chameleon ZPT template, given a Chameleon
-template file named ``foo.pt`` and a macro named ``bar`` defined within it
-(e.g. ``<div metal:define-macro="bar">...</div>``), you can configure the
-template as a :term:`renderer` like so:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import view_config
-
- @view_config(renderer='foo#bar.pt')
- def my_view(request):
- return {'project':'my project'}
-
-The above will render only the ``bar`` macro defined within the ``foo.pt``
-template instead of the entire template.
-
-.. versionadded:: 1.4
-
-.. index::
- single: Chameleon text templates
-
-.. _chameleon_text_templates:
-
-Templating with :term:`Chameleon` Text Templates
-------------------------------------------------
-
-:app:`Pyramid` also allows for the use of templates which are
-composed entirely of non-XML text via :term:`Chameleon`. To do so,
-you can create templates that are entirely composed of text except for
-``${name}`` -style substitution points.
-
-Here's an example usage of a Chameleon text template. Create a file
-on disk named ``mytemplate.txt`` in your project's ``templates``
-directory with the following contents:
-
-.. code-block:: text
-
- Hello, ${name}!
-
-Then in your project's ``views.py`` module, you can create a view
-which renders this template:
-
-.. code-block:: python
- :linenos:
-
- from pyramid.view import view_config
-
- @view_config(renderer='templates/mytemplate.txt')
- def my_view(request):
- return {'name':'world'}
-
-When the template is rendered, it will show:
-
-.. code-block:: text
-
- Hello, world!
-
-See also :ref:`built_in_renderers` for more general information about
-renderers, including Chameleon text renderers.
-
-.. index::
- single: template renderer side effects
-
-Side Effects of Rendering a Chameleon Template
-----------------------------------------------
-
-When a Chameleon template is rendered from a file, the templating
-engine writes a file in the same directory as the template file itself
-as a kind of cache, in order to do less work the next time the
-template needs to be read from disk. If you see "strange" ``.py``
-files showing up in your ``templates`` directory (or otherwise
-directly "next" to your templates), it is due to this feature.
-
-If you're using a version control system such as Subversion, you
-should configure it to ignore these files. Here's the contents of the
-author's ``svn propedit svn:ignore .`` in each of my ``templates``
-directories.
-
-.. code-block:: text
-
- *.pt.py
- *.txt.py
-
-Note that I always name my Chameleon ZPT template files with a ``.pt``
-extension and my Chameleon text template files with a ``.txt``
-extension so that these ``svn:ignore`` patterns work.
-
.. index::
pair: debugging; templates
+.. _debugging_templates:
+
Debugging Templates
-------------------
@@ -642,107 +390,6 @@ The output tells you which template the error occurred in, as well as
displaying the arguments passed to the template itself.
.. index::
- single: template internationalization
- single: internationalization (of templates)
-
-:term:`Chameleon` Template Internationalization
------------------------------------------------
-
-See :ref:`chameleon_translation_strings` for information about
-supporting internationalized units of text within :term:`Chameleon`
-templates.
-
-.. index::
- single: Mako
-
-.. _mako_templates:
-
-Templating With Mako Templates
-------------------------------
-
-:term:`Mako` is a templating system written by Mike Bayer. :app:`Pyramid`
-has built-in bindings for the Mako templating system. The language
-definition documentation for Mako templates is available from `the Mako
-website <http://www.makotemplates.org/>`_.
-
-To use a Mako template, given a :term:`Mako` template file named ``foo.mak``
-in the ``templates`` subdirectory in your application package named
-``mypackage``, you can configure the template as a :term:`renderer` like so:
-
-.. code-block:: python
- :linenos:
-
- 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 configuration's ``ini`` file:
-
-.. code-block:: ini
-
- 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
-:ref:`mako_template_renderer_settings` for more information about the
-``mako.directories`` setting and other Mako-related settings that can be
-placed into the application's ``ini`` file.
-
-.. index::
- single: Mako template (sample)
-
-A Sample Mako Template
-~~~~~~~~~~~~~~~~~~~~~~
-
-Here's what a simple :term:`Mako` template used under :app:`Pyramid` might
-look like:
-
-.. code-block:: xml
- :linenos:
-
- <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 doesn't use any advanced features of Mako, only the
-``${}`` replacement syntax for names that are passed in as
-:term:`renderer globals`. See the `the Mako documentation
-<http://www.makotemplates.org/>`_ to use more advanced features.
-
-Using A Mako def name Within a Renderer Name
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Sommetime you'd like to render a ``def`` inside of a Mako template instead of
-the full Mako template. To render a def inside a Mako template, given a
-:term:`Mako` template file named ``foo.mak`` and a def named ``bar``, you can
-configure the template as a :term:`renderer` like so:
-
-.. code-block:: python
- :linenos:
-
- 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.
-
-.. versionadded:: 1.4
-
-.. index::
single: automatic reloading of templates
single: template automatic reload
@@ -757,9 +404,11 @@ appear immediately without needing to restart the application process.
environment so that a change to a template will be automatically
detected, and the template will be reloaded on the next rendering.
-.. warning:: Auto-template-reload behavior is not recommended for
- production sites as it slows rendering slightly; it's
- usually only desirable during development.
+.. warning::
+
+ Auto-template-reload behavior is not recommended for
+ production sites as it slows rendering slightly; it's
+ usually only desirable during development.
In order to turn on automatic reloading of templates, you can use an
environment variable, or a configuration file setting.
@@ -770,31 +419,48 @@ variable set to ``1``, For example:
.. code-block:: text
- $ PYRAMID_RELOAD_TEMPLATES=1 $VENV/bin/pserve myproject.ini
+ $ PYRAMID_RELOAD_TEMPLATES=1 $VENV/bin/pserve myproject.ini
To use a setting in the application ``.ini`` file for the same
purpose, set the ``pyramid.reload_templates`` key to ``true`` within the
application's configuration section, e.g.:
.. code-block:: ini
- :linenos:
+ :linenos:
- [app:main]
- use = egg:MyProject
- pyramid.reload_templates = true
+ [app:main]
+ use = egg:MyProject
+ pyramid.reload_templates = true
.. index::
single: template system bindings
+ single: Chameleon
single: Jinja2
+ single: Mako
.. _available_template_system_bindings:
Available Add-On Template System Bindings
-----------------------------------------
-Jinja2 template bindings are available for :app:`Pyramid` in the
-``pyramid_jinja2`` package. You can get the latest release of
-this package from the
-`Python package index <http://pypi.python.org/pypi/pyramid_jinja2>`_
-(pypi).
+The Pylons Project maintains several packages providing bindings to different
+templating languages including the following:
+
++------------------------------+------------------------------+
+| Template Language | Pyramid Bindings |
++==============================+==============================+
+| Chameleon_ | pyramid_chameleon_ |
++------------------------------+------------------------------+
+| Jinja2_ | pyramid_jinja2_ |
++------------------------------+------------------------------+
+| Mako_ | pyramid_mako_ |
++------------------------------+------------------------------+
+
+.. _Chameleon: http://chameleon.readthedocs.org/en/latest/
+.. _pyramid_chameleon: https://pypi.python.org/pypi/pyramid_chameleon
+
+.. _Jinja2: http://jinja.pocoo.org/docs/
+.. _pyramid_jinja2: https://pypi.python.org/pypi/pyramid_jinja2
+.. _Mako: http://www.makotemplates.org/
+.. _pyramid_mako: https://pypi.python.org/pypi/pyramid_mako
diff --git a/docs/narr/testing.rst b/docs/narr/testing.rst
index bfb1287d9..88d6904c7 100644
--- a/docs/narr/testing.rst
+++ b/docs/narr/testing.rst
@@ -125,7 +125,7 @@ method attached to ``MyTest`` will use an isolated registry.
The :func:`~pyramid.testing.setUp` and :func:`~pyramid.testing.tearDown`
functions accepts various arguments that influence the environment of the
-test. See the :ref:`testing_module` chapter for information about the extra
+test. See the :ref:`testing_module` API for information about the extra
arguments supported by these functions.
If you also want to make :func:`~pyramid.threadlocal.get_current_request` return something
@@ -202,7 +202,7 @@ any ``get_current*`` function.
Using the ``Configurator`` and ``pyramid.testing`` APIs in Unit Tests
---------------------------------------------------------------------
-The ``Configurator`` API and the ``pyramid.testing`` module provide a number
+The ``Configurator`` API and the :mod:`pyramid.testing` module provide a number
of functions which can be used during unit testing. These functions make
:term:`configuration declaration` calls to the current :term:`application
registry`, but typically register a "stub" or "dummy" feature in place of the
@@ -222,6 +222,12 @@ function.
raise HTTPForbidden
return {'greeting':'hello'}
+.. note::
+
+ This code implies that you have defined a renderer imperatively in a
+ relevant :class:`pyramid.config.Configurator` instance,
+ otherwise it would fail when run normally.
+
Without doing anything special during a unit test, the call to
:func:`~pyramid.security.has_permission` in this view function will always
return a ``True`` value. When a :app:`Pyramid` application starts normally,
@@ -291,7 +297,7 @@ function is called, :func:`pyramid.security.has_permission` will call the
access. We check that the view function raises a
:exc:`~pyramid.httpexceptions.HTTPForbidden` error.
-The second test method, named ``test_view_fn_allowed`` tests the alternate
+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
:meth:`~pyramid.config.Configurator.testing_securitypolicy` to obtain this
@@ -373,7 +379,7 @@ after accessing some values that require a fully set up environment.
result = my_view(request)
self.assertEqual(result.status, '200 OK')
body = result.app_iter[0]
- self.failUnless('Welcome to' in body)
+ self.assertTrue('Welcome to' in body)
self.assertEqual(len(result.headerlist), 2)
self.assertEqual(result.headerlist[0],
('Content-Type', 'text/html; charset=UTF-8'))
@@ -416,7 +422,7 @@ functional testing package written by Ian Bicking.
def test_root(self):
res = self.testapp.get('/', status=200)
- self.failUnless('Pyramid' in res.body)
+ self.assertTrue('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 :term:`WebTest`
diff --git a/docs/narr/traversal.rst b/docs/narr/traversal.rst
index 2eb6ece13..454bb5620 100644
--- a/docs/narr/traversal.rst
+++ b/docs/narr/traversal.rst
@@ -146,35 +146,15 @@ refer to a root factory defined in a different module.
If no :term:`root factory` is passed to the :app:`Pyramid`
:term:`Configurator` constructor, or if the ``root_factory`` value
-specified is ``None``, a *default* root factory is used. The default
+specified is ``None``, a :term:`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.
-
-.. sidebar:: Emulating the Default Root Factory
-
- For purposes of understanding the default root factory better, we'll note
- that you can emulate the default root factory by using this code as an
- explicit root factory in your application setup:
-
- .. code-block:: python
- :linenos:
-
- class Root(object):
- def __init__(self, request):
- pass
-
- config = Configurator(root_factory=Root)
-
- The default root factory is just a really stupid object that has no
- behavior or state. Using :term:`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 you're developing an application using
- :term:`URL dispatch`.
+complicated than the above ``Root`` class; in particular it may be associated
+with a database connection or another persistence mechanism. The above
+``Root`` class is analogous to the default root factory present in Pyramid. The
+default root factory is very simple and not very useful.
.. note::
@@ -289,7 +269,7 @@ system uses this algorithm to find a :term:`context` resource and a
return resource "C".
#. Traversal ends when a) the entire path is exhausted or b) when any
- resouce raises a :exc:`KeyError` from its ``__getitem__`` or c) when any
+ resource raises a :exc:`KeyError` from its ``__getitem__`` or c) when any
non-final path element traversal does not have a ``__getitem__`` method
(resulting in a :exc:`AttributeError`) or d) when any path element is
prefixed with the set of characters ``@@`` (indicating that the characters
diff --git a/docs/narr/upgrading.rst b/docs/narr/upgrading.rst
index ca6dc565b..64343ca3e 100644
--- a/docs/narr/upgrading.rst
+++ b/docs/narr/upgrading.rst
@@ -150,7 +150,7 @@ do things the newer way:
.. code-block:: python
:linenos:
- from pyramid.view. import view_config
+ from pyramid.view import view_config
from pyramid.static import static_view
myview = static_view('static', 'static', use_subpath=True)
diff --git a/docs/narr/urldispatch.rst b/docs/narr/urldispatch.rst
index 18cb3e4db..61849c3c0 100644
--- a/docs/narr/urldispatch.rst
+++ b/docs/narr/urldispatch.rst
@@ -105,6 +105,7 @@ to using the previous combination of ``add_route`` and ``add_view``.
.. _route_pattern_syntax:
+
Route Pattern Syntax
~~~~~~~~~~~~~~~~~~~~
@@ -127,6 +128,10 @@ and:
/{foo}/bar/baz
+If a pattern is a valid URL it won't be ever matched against an incoming
+request. Instead it can be useful for generating external URLs. See
+:ref:`External routes <external_route_narr>` for details.
+
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
@@ -394,13 +399,6 @@ process. Examples of route predicate arguments are ``pattern``, ``xhr``, and
Other arguments are ``name`` and ``factory``. These arguments represent
neither predicates nor view configuration information.
-.. warning::
-
- Some arguments are view-configuration related arguments, such as
- ``view_renderer``. These only have an effect when the route configuration
- names a ``view`` and these arguments have been deprecated as of
- :app:`Pyramid` 1.1.
-
.. index::
single: route matching
@@ -754,9 +752,39 @@ other non-``name`` and non-``pattern`` arguments to
exception to this rule is use of the ``pregenerator`` argument, which is not
ignored when ``static`` is ``True``.
+:ref:`External routes <external_route_narr>` are implicitly static.
+
.. versionadded:: 1.1
the ``static`` argument to :meth:`~pyramid.config.Configurator.add_route`
+.. _external_route_narr:
+
+
+External Routes
+---------------
+
+.. versionadded:: 1.5
+
+Route patterns that are valid URLs, are treated as external routes. Like
+:ref:`static routes <static_route_narr>` they are useful for URL generation
+purposes only and are never considered for matching at request time.
+
+.. code-block:: python
+ :linenos:
+
+ >>> config = Configurator()
+ >>> config.add_route('youtube', 'https://youtube.com/watch/{video_id}')
+ ...
+ >>> request.route_url('youtube', video_id='oHg5SJYRHA0')
+ >>> "https://youtube.com/watch/oHg5SJYRHA0"
+
+Most pattern replacements and calls to
+:meth:`pyramid.request.Request.route_url` will work as expected. However, calls
+to :meth:`pyramid.request.Request.route_path` against external patterns will
+raise an exception, and passing ``_app_url`` to
+:meth:`~pyramid.request.Request.route_url` to generate a URL against a route
+that has an external pattern will also raise an exception.
+
.. index::
single: redirecting to slash-appended routes
@@ -865,7 +893,7 @@ Debugging Route Matching
------------------------
It's useful to be able to take a peek under the hood when requests that enter
-your application arent matching your routes as you expect them to. To debug
+your application aren't matching your routes as you expect them to. To debug
route matching, use the ``PYRAMID_DEBUG_ROUTEMATCH`` environment variable or the
``pyramid.debug_routematch`` configuration file setting (set either to ``true``).
Details of the route matching decision for a particular request to the
diff --git a/docs/narr/viewconfig.rst b/docs/narr/viewconfig.rst
index 241ce62b5..7c76116f7 100644
--- a/docs/narr/viewconfig.rst
+++ b/docs/narr/viewconfig.rst
@@ -290,9 +290,9 @@ configured view.
of the ``REQUEST_METHOD`` of the :term:`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
- :term:`request` has a key in the ``request.params`` dictionary (an HTTP
+ 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
+ :term:`request` has a key in the ``request.params`` dictionary (an HTTP
``GET`` or ``POST`` variable) that has a name which matches the
supplied value.
@@ -306,8 +306,6 @@ configured view.
consideration of keys and values in the ``request.params`` dictionary.
``match_param``
- .. versionadded:: 1.2
-
This param may be either a single string of the format "key=value" or a
dict of key/value pairs.
@@ -324,6 +322,8 @@ configured view.
If ``match_param`` is not supplied, the view will be invoked without
consideration of the keys and values in ``request.matchdict``.
+ .. versionadded:: 1.2
+
``containment``
This value should be a reference to a Python class or :term:`interface`
that a parent object in the context resource's :term:`lineage` must provide
@@ -465,6 +465,36 @@ configured view.
.. versionadded:: 1.4a1
+Inverting Predicate Values
+++++++++++++++++++++++++++
+
+You can invert the meaning of any predicate value by wrapping it in a call to
+:class:`pyramid.config.not_`.
+
+.. code-block:: python
+ :linenos:
+
+ from pyramid.config import not_
+
+ config.add_view(
+ 'mypackage.views.my_view',
+ route_name='ok',
+ request_method=not_('POST')
+ )
+
+The above example will ensure that the view is called if the request method
+is *not* ``POST``, at least if no other view is more specific.
+
+This technique of wrapping a predicate value in ``not_`` can be used anywhere
+predicate values are accepted:
+
+- :meth:`pyramid.config.Configurator.add_view`
+
+- :meth:`pyramid.view.view_config`
+
+.. versionadded:: 1.5
+
+
.. index::
single: view_config decorator
@@ -505,7 +535,7 @@ configuration stanza:
.. code-block:: python
:linenos:
- config.add_view('mypackage.views.my_view', route_name='ok',
+ config.add_view('mypackage.views.my_view', route_name='ok',
request_method='POST', permission='read')
All arguments to ``view_config`` may be omitted. For example:
@@ -792,7 +822,7 @@ of this:
def delete(self):
return Response('delete')
- if __name__ == '__main__':
+ def main(global_config, **settings):
config = Configurator()
config.add_route('rest', '/rest')
config.add_view(
@@ -801,9 +831,10 @@ of this:
RESTView, route_name='rest', attr='post', request_method='POST')
config.add_view(
RESTView, route_name='rest', attr='delete', request_method='DELETE')
+ return config.make_wsgi_app()
-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
+To reduce the amount of repetition in the ``config.add_view`` statements, we
+can move the ``route_name='rest'`` argument to a ``@view_defaults`` class
decorator on the RESTView class:
.. code-block:: python
@@ -827,12 +858,13 @@ decorator on the RESTView class:
def delete(self):
return Response('delete')
- if __name__ == '__main__':
+ def main(global_config, **settings):
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')
+ return config.make_wsgi_app()
:class:`pyramid.view.view_defaults` accepts the same set of arguments that
:class:`pyramid.view.view_config` does, and they have the same meaning. Each
@@ -976,6 +1008,8 @@ invoked as the result of the ``http_cache`` argument to view configuration.
.. index::
pair: view configuration; debugging
+.. _debugging_view_configuration:
+
Debugging View Configuration
----------------------------
diff --git a/docs/narr/webob.rst b/docs/narr/webob.rst
index c0ca450b1..f0a4b5a0b 100644
--- a/docs/narr/webob.rst
+++ b/docs/narr/webob.rst
@@ -287,7 +287,7 @@ When such a request reaches a view in your application, the
@view_config(renderer='string')
def aview(request):
- print request.json_body
+ print(request.json_body)
return 'OK'
For the above view, printed to the console will be:
diff --git a/docs/quick_tour.rst b/docs/quick_tour.rst
new file mode 100644
index 000000000..2db18c8a7
--- /dev/null
+++ b/docs/quick_tour.rst
@@ -0,0 +1,890 @@
+.. _quick_tour:
+
+=====================
+Quick Tour of Pyramid
+=====================
+
+Pyramid lets you start small and finish big. This *Quick Tour* of Pyramid is
+for those who want to evaluate Pyramid, whether you are new to Python
+web frameworks, or a pro in a hurry. For more detailed treatment of
+each topic, give the :ref:`quick_tutorial` a try.
+
+Installation
+============
+
+Once you have a standard Python environment setup, getting started with
+Pyramid is a breeze. Unfortunately "standard" is not so simple in Python.
+For this Quick Tour, it means:
+`Python <http://www.python.org/download/releases/>`_, a
+`virtual environment <http://docs.python.org/dev/library/venv.html>`_
+(or `virtualenv for Python 2.7 <https://pypi.python.org/pypi/virtualenv>`_),
+and `setuptools <https://pypi.python.org/pypi/setuptools/>`_.
+
+As an example, for Python 3.3+ on Linux:
+
+.. parsed-literal::
+
+ $ pyvenv env33
+ $ wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | env33/bin/python
+ $ env33/bin/easy_install "pyramid==\ |release|\ "
+
+For Windows:
+
+.. parsed-literal::
+
+ # Use your browser to download:
+ # https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
+ c:\\> c:\\Python33\\python -m venv env33
+ c:\\> env33\\Scripts\\python ez_setup.py
+ c:\\> env33\\Scripts\\easy_install "pyramid==\ |release|\ "
+
+Of course Pyramid runs fine on Python 2.6+, as do the examples in this
+*Quick Tour*. We're just showing Python 3 a little love (Pyramid had
+production support in October 2011.)
+
+.. note::
+
+ Why ``easy_install`` and not ``pip``? Pyramid encourages use of namespace
+ packages which, until recently, ``pip`` didn't permit. Also, Pyramid has
+ some optional C extensions for performance. With ``easy_install``, Windows
+ users can get these extensions without needing a C compiler.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial section on Requirements <qtut_requirements>`,
+ :ref:`installing_unix`,
+ :ref:`Before You Install <installing_chapter>`, and
+ :ref:`Installing Pyramid on a Windows System <installing_windows>`
+
+Hello World
+===========
+
+Microframeworks have shown that learning starts best from a very small
+first step. Here's a tiny application in Pyramid:
+
+.. literalinclude:: quick_tour/hello_world/app.py
+ :linenos:
+
+This simple example is easy to run. Save this as ``app.py`` and run it:
+
+.. code-block:: bash
+
+ $ python ./app.py
+
+Next, open `http://localhost:6543/ <http://localhost:6543/>`_ in a
+browser and you will see the ``Hello World!`` message.
+
+New to Python web programming? If so, some lines in module merit
+explanation:
+
+#. *Line 10*. The ``if __name__ == '__main__':`` is Python's way of
+ saying "Start here when running from the command line".
+
+#. *Lines 11-13*. Use Pyramid's :term:`configurator` to connect
+ :term:`view` code to particular URL :term:`route`.
+
+#. *Lines 6-7*. Implement the view code that generates the
+ :term:`response`.
+
+#. *Lines 14-16*. Publish a :term:`WSGI` app using an HTTP server.
+
+As shown in this example, the :term:`configurator` plays a central role
+in Pyramid development. Building an application from loosely-coupled
+parts via :doc:`../narr/configuration` is a central idea in Pyramid,
+one that we will revisit regurlarly in this *Quick Tour*.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Hello World <qtut_hello_world>`,
+ :ref:`firstapp_chapter`, and
+ :ref:`Single File Tasks tutorial <tutorials:single-file-tutorial>`
+
+Handling Web Requests and Responses
+===================================
+
+Developing for the web means processing web requests. As this is a
+critical part of a web application, web developers need a robust,
+mature set of software for web requests.
+
+Pyramid has always fit nicely into the existing world of Python web
+development (virtual environments, packaging, scaffolding,
+first to embrace Python 3, etc.) For request handling, Pyramid turned
+to the well-regarded :term:`WebOb` Python library for request and
+response handling. In our example
+above, Pyramid hands ``hello_world`` a ``request`` that is
+:ref:`based on WebOb <webob_chapter>`.
+
+Let's see some features of requests and responses in action:
+
+.. literalinclude:: quick_tour/requests/app.py
+ :pyobject: hello_world
+
+In this Pyramid view, we get the URL being visited from ``request.url``.
+Also, if you visited ``http://localhost:6543/?name=alice``,
+the name is included in the body of the response::
+
+ URL http://localhost:6543/?name=alice with name: alice
+
+Finally, we set the response's content type and return the Response.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Request and Response <qtut_request_response>`
+ and
+ :ref:`webob_chapter`
+
+Views
+=====
+
+For the examples above, the ``hello_world`` function is a "view". In
+Pyramid, views are the primary way to accept web requests and return
+responses.
+
+So far our examples place everything in one file:
+
+- the view function
+
+- its registration with the configurator
+
+- the route to map it to a URL
+
+- the WSGI application launcher
+
+Let's move the views out to their own ``views.py`` module and change
+the ``app.py`` to scan that module, looking for decorators that setup
+the views. First, our revised ``app.py``:
+
+.. literalinclude:: quick_tour/views/app.py
+ :linenos:
+
+We added some more routes, but we also removed the view code.
+Our views, and their registrations (via decorators) are now in a module
+``views.py`` which is scanned via ``config.scan('views')``.
+
+We now have a ``views.py`` module that is focused on handling requests
+and responses:
+
+.. literalinclude:: quick_tour/views/views.py
+ :linenos:
+
+We have 4 views, each leading to the other. If you start at
+``http://localhost:6543/``, you get a response with a link to the next
+view. The ``hello_view`` (available at the URL ``/howdy``) has a link
+to the ``redirect_view``, which shows issuing a redirect to the final
+view.
+
+Earlier we saw ``config.add_view`` as one way to configure a view. This
+section introduces ``@view_config``. Pyramid's configuration supports
+:term:`imperative configuration`, such as the ``config.add_view`` in
+the previous example. You can also use :term:`declarative
+configuration`, in which a Python :term:`decorator` is placed on the
+line above the view. Both approaches result in the same final
+configuration, thus usually, it is simply a matter of taste.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Views <qtut_views>`,
+ :doc:`../narr/views`,
+ :doc:`../narr/viewconfig`, and
+ :ref:`debugging_view_configuration`
+
+Routing
+=======
+
+Writing web applications usually means sophisticated URL design. We
+just saw some Pyramid machinery for requests and views. Let's look at
+features that help in routing.
+
+Above we saw the basics of routing URLs to views in Pyramid:
+
+- Your project's "setup" code registers a route name to be used when
+ matching part of the URL
+
+- Elsewhere, a view is configured to be called for that route name
+
+.. note::
+
+ Why do this twice? Other Python web frameworks let you create a
+ route and associate it with a view in one step. As
+ illustrated in :ref:`routes_need_ordering`, multiple routes might
+ match the same URL pattern. Rather than provide ways to help guess,
+ Pyramid lets you be explicit in ordering. Pyramid also gives
+ facilities to avoid the problem.
+
+What if we want part of the URL to be available as data in my view? This
+route declaration:
+
+.. literalinclude:: quick_tour/routing/app.py
+ :start-after: Start Route 1
+ :end-before: End Route 1
+
+With this, URLs such as ``/howdy/amy/smith`` will assign ``amy`` to
+``first`` and ``smith`` to ``last``. We can then use this data in our
+view:
+
+.. literalinclude:: quick_tour/routing/views.py
+ :start-after: Start Route 1
+ :end-before: End Route 1
+
+``request.matchdict`` contains values from the URL that match the
+"replacement patterns" (the curly braces) in the route declaration.
+This information can then be used in your view.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Routing <qtut_routing>`,
+ :doc:`../narr/urldispatch`,
+ :ref:`debug_routematch_section`, and
+ :doc:`../narr/router`
+
+Templating
+==========
+
+Ouch. We have been making our own ``Response`` and filling the response
+body with HTML. You usually won't embed an HTML string directly in
+Python, but instead, will use a templating language.
+
+Pyramid doesn't mandate a particular database system, form library,
+etc. It encourages replaceability. This applies equally to templating,
+which is fortunate: developers have strong views about template
+languages. That said, the Pylons Project officially supports bindings for
+Chameleon, Jinja2 and Mako, so in this step, let's use Chameleon as an
+example:
+
+.. literalinclude:: quick_tour/templating/views.py
+ :start-after: Start View 1
+ :end-before: End View 1
+
+Ahh, that looks better. We have a view that is focused on Python code.
+Our ``@view_config`` decorator specifies a :term:`renderer` that points
+our template file. Our view then simply returns data which is then
+supplied to our template:
+
+.. literalinclude:: quick_tour/templating/hello_world.pt
+ :language: html
+
+Since our view returned ``dict(name=request.matchdict['name'])``,
+we can use ``name`` as a variable in our template via
+``${name}``.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Templating <qtut_templating>`,
+ :doc:`../narr/templates`,
+ :ref:`debugging_templates`, and
+ :ref:`available_template_system_bindings`
+
+Templating With ``jinja2``
+==========================
+
+We just said Pyramid doesn't prefer one templating language over
+another. Time to prove it. Jinja2 is a popular templating system,
+modelled after Django's templates. Let's add ``pyramid_jinja2``,
+a Pyramid :term:`add-on` which enables Jinja2 as a :term:`renderer` in
+our Pyramid applications:
+
+.. code-block:: bash
+
+ $ easy_install pyramid_jinja2
+
+With the package installed, we can include the template bindings into
+our configuration:
+
+.. code-block:: python
+
+ config.include('pyramid_jinja2')
+
+The only change in our view...point the renderer at the ``.jinja2`` file:
+
+.. literalinclude:: quick_tour/jinja2/views.py
+ :start-after: Start View 1
+ :end-before: End View 1
+
+Our Jinja2 template is very similar to our previous template:
+
+.. literalinclude:: quick_tour/jinja2/hello_world.jinja2
+ :language: html
+
+Pyramid's templating add-ons register a new kind of renderer into your
+application. The renderer registration maps to different kinds of
+filename extensions. In this case, changing the extension from ``.pt``
+to ``.jinja2`` passed the view response through the ``pyramid_jinja2``
+renderer.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Jinja2 <qtut_jinja2>`,
+ `Jinja2 homepage <http://jinja.pocoo.org/>`_, and
+ :ref:`pyramid_jinja2 Overview <jinja2:overview>`
+
+Static Assets
+=============
+
+Of course the Web is more than just markup. You need static assets:
+CSS, JS, and images. Let's point our web app at a directory where
+Pyramid will serve some static assets. First, another call to the
+:term:`configurator`:
+
+.. literalinclude:: quick_tour/static_assets/app.py
+ :start-after: Start Static 1
+ :end-before: End Static 1
+
+This tells our WSGI application to map requests under
+``http://localhost:6543/static/`` to files and directories inside a
+``static`` directory alongside our Python module.
+
+Next, make a directory ``static`` and place ``app.css`` inside:
+
+.. literalinclude:: quick_tour/static_assets/static/app.css
+ :language: css
+
+All we need to do now is point to it in the ``<head>`` of our Jinja2
+template:
+
+.. literalinclude:: quick_tour/static_assets/hello_world.pt
+ :language: html
+ :start-after: Start Link 1
+ :end-before: End Link 1
+
+This link presumes that our CSS is at a URL starting with ``/static/``.
+What if the site is later moved under ``/somesite/static/``? Or perhaps
+web developer changes the arrangement on disk? Pyramid gives a helper
+that provides flexibility on URL generation:
+
+.. literalinclude:: quick_tour/static_assets/hello_world.pt
+ :language: html
+ :start-after: Start Link 2
+ :end-before: End Link 2
+
+By using ``request.static_url`` to generate the full URL to the static
+assets, you both ensure you stay in sync with the configuration and
+gain refactoring flexibility later.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Static Assets <qtut_static_assets>`,
+ :doc:`../narr/assets`,
+ :ref:`preventing_http_caching`, and
+ :ref:`influencing_http_caching`
+
+Returning JSON
+==============
+
+Modern web apps are more than rendered HTML. Dynamic pages now use
+JavaScript to update the UI in the browser by requesting server data as
+JSON. Pyramid supports this with a JSON renderer:
+
+.. literalinclude:: quick_tour/json/views.py
+ :start-after: Start View 1
+ :end-before: End View 2
+
+This wires up a view that returns some data through the JSON
+:term:`renderer`, which calls Python's JSON support to serialize the data
+into JSON and set the appropriate HTTP headers.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial JSON <qtut_json>`,
+ :ref:`views_which_use_a_renderer`,
+ :ref:`json_renderer`, and
+ :ref:`adding_and_overriding_renderers`
+
+View Classes
+============
+
+So far our views have been simple, free-standing functions. Many times
+your views are related: different ways to look at or work on the same
+data or a REST API that handles multiple operations. Grouping these
+together as a
+:ref:`view class <class_as_view>` makes sense:
+
+- Group views
+
+- Centralize some repetitive defaults
+
+- Share some state and helpers
+
+The following shows a "Hello World" example with three operations: view
+a form, save a change, or press the delete button:
+
+.. literalinclude:: quick_tour/view_classes/views.py
+ :start-after: Start View 1
+ :end-before: End View 1
+
+As you can see, the three views are logically grouped together.
+Specifically:
+
+- The first view is returned when you go to ``/howdy/amy``. This URL is
+ mapped to the ``hello`` route that we centrally set using the optional
+ ``@view_defaults``.
+
+- The second view is returned when the form data contains a field with
+ ``form.edit``, such as clicking on
+ ``<input type="submit" name="form.edit" value="Save"/>``. This rule
+ is specified in the ``@view_config`` for that view.
+
+- The third view is returned when clicking on a button such
+ as ``<input type="submit" name="form.delete" value="Delete"/>``.
+
+Only one route needed, stated in one place atop the view class. Also,
+the assignment of the ``name`` is done in the ``__init__``. Our
+templates can then use ``{{ view.name }}``.
+
+Pyramid view classes, combined with built-in and custom predicates,
+have much more to offer:
+
+- All the same view configuration parameters as function views
+
+- One route leading to multiple views, based on information in the
+ request or data such as ``request_param``, ``request_method``,
+ ``accept``, ``header``, ``xhr``, ``containment``, and
+ ``custom_predicates``
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial View Classes <qtut_view_classes>`,
+ :ref:`Quick Tutorial More View Classes <qtut_more_view_classes>`, and
+ :ref:`class_as_view`
+
+Quick Project Startup with Scaffolds
+====================================
+
+So far we have done all of our *Quick Glance* as a single Python file.
+No Python packages, no structure. Most Pyramid projects, though,
+aren't developed this way.
+
+To ease the process of getting started, Pyramid provides *scaffolds*
+that generate sample projects from templates in Pyramid and Pyramid
+add-ons. Pyramid's ``pcreate`` command can list the available scaffolds:
+
+.. code-block:: bash
+
+ $ pcreate --list
+ Available scaffolds:
+ alchemy: Pyramid SQLAlchemy project using url dispatch
+ pyramid_jinja2_starter: pyramid jinja2 starter project
+ starter: Pyramid starter project
+ zodb: Pyramid ZODB project using traversal
+
+The ``pyramid_jinja2`` add-on gave us a scaffold that we can use. From
+the parent directory of where we want our Python package to be generated,
+let's use that scaffold to make our project:
+
+.. code-block:: bash
+
+ $ pcreate --scaffold pyramid_jinja2_starter hello_world
+
+We next use the normal Python development to setup our package for
+development:
+
+.. code-block:: bash
+
+ $ cd hello_world
+ $ python ./setup.py develop
+
+We are moving in the direction of a full-featured Pyramid project,
+with a proper setup for Python standards (packaging) and Pyramid
+configuration. This includes a new way of running your application:
+
+.. code-block:: bash
+
+ $ pserve development.ini
+
+Let's look at ``pserve`` and configuration in more depth.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Scaffolds <qtut_scaffolds>`,
+ :ref:`project_narr`, and
+ :doc:`../narr/scaffolding`
+
+Application Running with ``pserve``
+===================================
+
+Prior to scaffolds, our project mixed a number of operations details
+into our code. Why should my main code care which HTTP server I want and
+what port number to run on?
+
+``pserve`` is Pyramid's application runner, separating operational
+details from your code. When you install Pyramid, a small command
+program called ``pserve`` is written to your ``bin`` directory. This
+program is an executable Python module. It's very small, getting most
+of its brains via import.
+
+You can run ``pserve`` with ``--help`` to see some of its options.
+Doing so reveals that you can ask ``pserve`` to watch your development
+files and reload the server when they change:
+
+.. code-block:: bash
+
+ $ pserve development.ini --reload
+
+The ``pserve`` command has a number of other options and operations.
+Most of the work, though, comes from your project's wiring, as
+expressed in the configuration file you supply to ``pserve``. Let's
+take a look at this configuration file.
+
+.. seealso:: See Also:
+ :ref:`what_is_this_pserve_thing`
+
+Configuration with ``.ini`` Files
+=================================
+
+Earlier in *Quick Glance* we first met Pyramid's configuration system.
+At that point we did all configuration in Python code. For example,
+the port number chosen for our HTTP server was right there in Python
+code. Our scaffold has moved this decision, and more, into the
+``development.ini`` file:
+
+.. literalinclude:: quick_tour/package/development.ini
+ :language: ini
+
+Let's take a quick high-level look. First, the ``.ini`` file is divided
+into sections:
+
+- ``[app:hello_world]`` configures our WSGI app
+
+- ``[pipeline:main]`` sets up our WSGI "pipeline"
+
+- ``[server:main]`` holds our WSGI server settings
+
+- Various sections afterwards configure our Python logging system
+
+We have a few decisions made for us in this configuration:
+
+#. *Choice of web server*. The ``use = egg:pyramid#wsgiref`` tell
+ ``pserve`` to the ``wsgiref`` server that is wrapped in the Pyramid
+ package.
+
+#. *Port number*. ``port = 6543`` tells ``wsgiref`` to listen on port
+ 6543.
+
+#. *WSGI app*. What package has our WSGI application in it?
+ ``use = egg:hello_world`` in the app section tells the
+ configuration what application to load.
+
+#. *Easier development by automatic template reloading*. In development
+ mode, you shouldn't have to restart the server when editing a Jinja2
+ template. ``reload_templates = true`` sets this policy,
+ which might be different in production.
+
+Additionally, the ``development.ini`` generated by this scaffold wired
+up Python's standard logging. We'll now see in the console, for example,
+a log on every request that comes in, as well traceback information.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Application Configuration <qtut_ini>`,
+ :ref:`environment_chapter` and
+ :doc:`../narr/paste`
+
+
+Easier Development with ``debugtoolbar``
+========================================
+
+As we introduce the basics we also want to show how to be productive in
+development and debugging. For example, we just discussed template
+reloading and earlier we showed ``--reload`` for application reloading.
+
+``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes
+several tools available in your browser. Adding it to your project
+illustrates several points about configuration.
+
+First, change your ``setup.py`` to say:
+
+.. literalinclude:: quick_tour/package/setup.py
+ :start-after: Start Requires
+ :end-before: End Requires
+
+...and re-run your setup:
+
+.. code-block:: bash
+
+ $ python ./setup.py develop
+
+The Python package was now installed into our environment. The package
+is a Pyramid add-on, which means we need to include its configuration
+into our web application. We could do this with imperative
+configuration, as we did above for the ``pyramid_jinja2`` add-on:
+
+.. literalinclude:: quick_tour/package/hello_world/__init__.py
+ :start-after: Start Include
+ :end-before: End Include
+
+Now that we have a configuration file, we can use the
+``pyramid.includes`` facility and place this in our
+``development.ini`` instead:
+
+.. literalinclude:: quick_tour/package/development.ini
+ :language: ini
+ :start-after: Start Includes
+ :end-before: End Includes
+
+You'll now see an attractive (and
+collapsible) menu in the right of your browser, providing introspective
+access to debugging information. Even better, if your web application
+generates an error, you will see a nice traceback on the screen. When
+you want to disable this toolbar, no need to change code: you can
+remove it from ``pyramid.includes`` in the relevant ``.ini``
+configuration file.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial
+ pyramid_debugtoolbar <qtut_debugtoolbar>` and
+ :ref:`pyramid_debugtoolbar <toolbar:overview>`
+
+Unit Tests and ``nose``
+=======================
+
+Yikes! We got this far and we haven't yet discussed tests. Particularly
+egregious, as Pyramid has had a deep commitment to full test coverage
+since before it was released.
+
+Our ``pyramid_jinja2_starter`` scaffold generated a ``tests.py`` module
+with one unit test in it. To run it, let's install the handy ``nose``
+test runner by editing ``setup.py``. While we're at it, we'll throw in
+the ``coverage`` tool which yells at us for code that isn't tested:
+
+.. code-block:: python
+
+ setup(name='hello_world',
+ # Some lines removed...
+ extras_require={
+ 'testing': ['nose', 'coverage'],
+ }
+ )
+
+We changed ``setup.py`` which means we need to re-run
+``python ./setup.py develop``. We can now run all our tests:
+
+.. code-block:: bash
+
+ $ nosetests hello_world/tests.py
+ .
+ Name Stmts Miss Cover Missing
+ ---------------------------------------------------
+ hello_world 12 8 33% 11-23
+ hello_world.models 5 1 80% 8
+ hello_world.tests 14 0 100%
+ hello_world.views 4 0 100%
+ ---------------------------------------------------
+ TOTAL 35 9 74%
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.931s
+
+ OK
+
+Our unit test passed. What did our test look like?
+
+.. literalinclude:: quick_tour/package/hello_world/tests.py
+
+Pyramid supplies helpers for test writing, which we use in the
+test setup and teardown. Our one test imports the view,
+makes a dummy request, and sees if the view returns what we expected.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Unit Testing <qtut_unit_testing>`,
+ :ref:`Quick Tutorial Functional Testing <qtut_functional_testing>`,
+ and
+ :ref:`testing_chapter`
+
+Logging
+=======
+
+It's important to know what is going on inside our web application.
+In development we might need to collect some output. In production,
+we might need to detect situations when other people use the site. We
+need *logging*.
+
+Fortunately Pyramid uses the normal Python approach to logging. The
+scaffold generated, in your ``development.ini``, a number of lines that
+configure the logging for you to some reasonable defaults. You then see
+messages sent by Pyramid (for example, when a new request comes in.)
+
+Maybe you would like to log messages in your code? In your Python
+module, import and setup the logging:
+
+.. literalinclude:: quick_tour/package/hello_world/views.py
+ :start-after: Start Logging 1
+ :end-before: End Logging 1
+
+You can now, in your code, log messages:
+
+.. literalinclude:: quick_tour/package/hello_world/views.py
+ :start-after: Start Logging 2
+ :end-before: End Logging 2
+
+This will log ``Some Message`` at a ``debug`` log level,
+to the application-configured logger in your ``development.ini``. What
+controls that? These sections in the configuration file:
+
+.. literalinclude:: quick_tour/package/development.ini
+ :language: ini
+ :start-after: Start Sphinx Include
+ :end-before: End Sphinx Include
+
+Our application, a package named ``hello_world``, is setup as a logger
+and configured to log messages at a ``DEBUG`` or higher level. When you
+visit ``http://localhost:6543`` your console will now show::
+
+ 2013-08-09 10:42:42,968 DEBUG [hello_world.views][MainThread] Some Message
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Logging <qtut_logging>` and
+ :ref:`logging_chapter`
+
+Sessions
+========
+
+When people use your web application, they frequently perform a task
+that requires semi-permanent data to be saved. For example, a shopping
+cart. This is called a :term:`session`.
+
+Pyramid has basic built-in support for sessions, with add-ons such as
+``pyramid_redis_sessions`` (or your own custom sessioning engine) that provide
+richer session support. Let's take a look at the
+:doc:`built-in sessioning support <../narr/sessions>`. In our
+``__init__.py`` we first import the kind of sessioning we want:
+
+.. literalinclude:: quick_tour/package/hello_world/__init__.py
+ :start-after: Start Sphinx Include 1
+ :end-before: End Sphinx Include 1
+
+.. warning::
+
+ As noted in the session docs, this example implementation is
+ not intended for use in settings with security implications.
+
+Now make a "factory" and pass it to the :term:`configurator`'s
+``session_factory`` argument:
+
+.. literalinclude:: quick_tour/package/hello_world/__init__.py
+ :start-after: Start Sphinx Include 2
+ :end-before: End Sphinx Include 2
+
+Pyramid's :term:`request` object now has a ``session`` attribute
+that we can use in our view code:
+
+.. literalinclude:: quick_tour/package/hello_world/views.py
+ :start-after: Start Sphinx Include 1
+ :end-before: End Sphinx Include 1
+
+With this, each reload will increase the counter displayed in our
+Jinja2 template:
+
+.. literalinclude:: quick_tour/package/hello_world/templates/mytemplate.jinja2
+ :language: jinja
+ :start-after: Start Sphinx Include 1
+ :end-before: End Sphinx Include 1
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Sessions <qtut_sessions>`,
+ :ref:`sessions_chapter`, :ref:`flash_messages`,
+ :ref:`session_module`, and :term:`pyramid_redis_sessions`.
+
+Databases
+=========
+
+Web applications mean data. Data means databases. Frequently SQL
+databases. SQL Databases frequently mean an "ORM"
+(object-relational mapper.) In Python, ORM usually leads to the
+mega-quality *SQLAlchemy*, a Python package that greatly eases working
+with databases.
+
+Pyramid and SQLAlchemy are great friends. That friendship includes a
+scaffold!
+
+.. code-block:: bash
+
+ $ pcreate --scaffold alchemy sqla_demo
+ $ cd sqla_demo
+ $ python setup.py develop
+
+We now have a working sample SQLAlchemy application with all
+dependencies installed. The sample project provides a console script to
+initialize a SQLite database with tables. Let's run it and then start
+the application:
+
+.. code-block:: bash
+
+ $ initialize_sqla_demo_db development.ini
+ $ pserve development.ini
+
+The ORM eases the mapping of database structures into a programming
+language. SQLAlchemy uses "models" for this mapping. The scaffold
+generated a sample model:
+
+.. literalinclude:: quick_tour/sqla_demo/sqla_demo/models.py
+ :start-after: Start Sphinx Include
+ :end-before: End Sphinx Include
+
+View code, which mediates the logic between web requests and the rest
+of the system, can then easily get at the data thanks to SQLAlchemy:
+
+.. literalinclude:: quick_tour/sqla_demo/sqla_demo/views.py
+ :start-after: Start Sphinx Include
+ :end-before: End Sphinx Include
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Databases <qtut_databases>`,
+ `SQLAlchemy <http://www.sqlalchemy.org/>`_,
+ :ref:`making_a_console_script`,
+ :ref:`bfg_sql_wiki_tutorial`, and
+ :ref:`Application Transactions With pyramid_tm <tm:overview>`
+
+Forms
+=====
+
+Developers have lots of opinions about web forms, and thus there are many
+form libraries for Python. Pyramid doesn't directly bundle a form
+library, but *Deform* is a popular choice for forms,
+along with its related *Colander* schema system.
+
+As an example, imagine we want a form that edits a wiki page. The form
+should have two fields on it, one of them a required title and the
+other a rich text editor for the body. With Deform we can express this
+as a Colander schema:
+
+.. code-block:: python
+
+ class WikiPage(colander.MappingSchema):
+ title = colander.SchemaNode(colander.String())
+ body = colander.SchemaNode(
+ colander.String(),
+ widget=deform.widget.RichTextWidget()
+ )
+
+With this in place, we can render the HTML for a form,
+perhaps with form data from an existing page:
+
+.. code-block:: python
+
+ form = self.wiki_form.render()
+
+We'd like to handle form submission, validation, and saving:
+
+.. code-block:: python
+
+ # Get the form data that was posted
+ controls = self.request.POST.items()
+ try:
+ # Validate and either raise a validation error
+ # or return deserialized data from widgets
+ appstruct = wiki_form.validate(controls)
+ except deform.ValidationFailure as e:
+ # Bail out and render form with errors
+ return dict(title=title, page=page, form=e.render())
+
+ # Change the content and redirect to the view
+ page['title'] = appstruct['title']
+ page['body'] = appstruct['body']
+
+Deform and Colander provide a very flexible combination for forms,
+widgets, schemas, and validation. Recent versions of Deform also
+include a :ref:`retail mode <deform:retail>` for gaining Deform
+features on custom forms.
+
+Also, the ``deform_bootstrap`` Pyramid add-on restyles the stock Deform
+widgets using attractive CSS from Bootstrap and more powerful widgets
+from Chosen.
+
+.. seealso:: See Also:
+ :ref:`Quick Tutorial Forms <qtut_forms>`,
+ :ref:`Deform <deform:overview>`,
+ :ref:`Colander <colander:overview>`, and
+ `deform_bootstrap <https://pypi.python.org/pypi/deform_bootstrap>`_
+
+Conclusion
+==========
+
+This *Quick Tour* covered a little about a lot. We introduced a long list
+of concepts in Pyramid, many of which are expanded on more fully in the
+Pyramid developer docs.
diff --git a/docs/quick_tour/awesome/CHANGES.txt b/docs/quick_tour/awesome/CHANGES.txt
new file mode 100644
index 000000000..ffa255da8
--- /dev/null
+++ b/docs/quick_tour/awesome/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/quick_tour/awesome/MANIFEST.in b/docs/quick_tour/awesome/MANIFEST.in
new file mode 100644
index 000000000..e78395da8
--- /dev/null
+++ b/docs/quick_tour/awesome/MANIFEST.in
@@ -0,0 +1,2 @@
+include *.txt *.ini *.cfg *.rst
+recursive-include awesome *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/quick_tour/awesome/README.txt b/docs/quick_tour/awesome/README.txt
new file mode 100644
index 000000000..f695286d9
--- /dev/null
+++ b/docs/quick_tour/awesome/README.txt
@@ -0,0 +1,4 @@
+awesome README
+
+
+
diff --git a/docs/quick_tour/awesome/awesome/__init__.py b/docs/quick_tour/awesome/awesome/__init__.py
new file mode 100644
index 000000000..408033997
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/__init__.py
@@ -0,0 +1,23 @@
+from pyramid.config import Configurator
+from pyramid_jinja2 import renderer_factory
+from awesome.models import get_root
+
+def main(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ settings = dict(settings)
+ settings.setdefault('jinja2.i18n.domain', 'awesome')
+
+ config = Configurator(root_factory=get_root, settings=settings)
+ config.add_translation_dirs('locale/')
+ config.include('pyramid_jinja2')
+
+ config.add_static_view('static', 'static')
+ config.add_view('awesome.views.my_view',
+ context='awesome.models.MyModel',
+ renderer="mytemplate.jinja2")
+
+ return config.make_wsgi_app()
diff --git a/docs/quick_tour/awesome/awesome/locale/awesome.pot b/docs/quick_tour/awesome/awesome/locale/awesome.pot
new file mode 100644
index 000000000..9c9460cb2
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/locale/awesome.pot
@@ -0,0 +1,21 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-05-12 09:14-0330\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+msgid "Hello!"
+msgstr ""
diff --git a/docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.mo b/docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.mo
new file mode 100644
index 000000000..40bf0c271
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.mo
Binary files differ
diff --git a/docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.po b/docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.po
new file mode 100644
index 000000000..0df243dba
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/locale/de/LC_MESSAGES/awesome.po
@@ -0,0 +1,21 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-05-12 09:14-0330\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+msgid "Hello!"
+msgstr "Hallo!"
diff --git a/docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.mo b/docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.mo
new file mode 100644
index 000000000..4fc438bfe
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.mo
Binary files differ
diff --git a/docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.po b/docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.po
new file mode 100644
index 000000000..dc0aae5d7
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/locale/fr/LC_MESSAGES/awesome.po
@@ -0,0 +1,21 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-05-12 09:14-0330\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+msgid "Hello!"
+msgstr "Bonjour!"
diff --git a/docs/quick_tour/awesome/awesome/models.py b/docs/quick_tour/awesome/awesome/models.py
new file mode 100644
index 000000000..edd361c9c
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/models.py
@@ -0,0 +1,8 @@
+class MyModel(object):
+ pass
+
+root = MyModel()
+
+
+def get_root(request):
+ return root
diff --git a/docs/quick_tour/awesome/awesome/static/favicon.ico b/docs/quick_tour/awesome/awesome/static/favicon.ico
new file mode 100644
index 000000000..71f837c9e
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/static/favicon.ico
Binary files differ
diff --git a/docs/quick_tour/awesome/awesome/static/logo.png b/docs/quick_tour/awesome/awesome/static/logo.png
new file mode 100644
index 000000000..88f5d9865
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/static/logo.png
Binary files differ
diff --git a/docs/quick_tour/awesome/awesome/static/pylons.css b/docs/quick_tour/awesome/awesome/static/pylons.css
new file mode 100644
index 000000000..42e2e320e
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/static/pylons.css
@@ -0,0 +1,73 @@
+html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
+vertical-align:baseline;background:transparent;}
+body{line-height:1;}
+ol,ul{list-style:none;}
+blockquote,q{quotes:none;}
+blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
+/* remember to define focus styles! */
+:focus{outline:0;}
+/* remember to highlight inserts somehow! */
+ins{text-decoration:none;}
+del{text-decoration:line-through;}
+/* tables still need 'cellspacing="0"' in the markup */
+table{border-collapse:collapse;border-spacing:0;}
+/* restyling */
+sub{vertical-align:sub;font-size:smaller;line-height:normal;}
+sup{vertical-align:super;font-size:smaller;line-height:normal;}
+/* lists */
+ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
+ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
+li{display:list-item;}
+/* nested lists have no top/bottom margins */
+ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
+/* 2 deep unordered lists use a circle */
+ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
+/* 3 deep (or more) unordered lists use a square */
+ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
+.hidden{display:none;}
+p{line-height:1.5em;}
+h1{font-size:1.75em;/* 28px */
+line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;/* 24px */
+line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;/* 20px */
+line-height:1.7em;font-family:helvetica,verdana;}
+h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
+html,body{width:100%;height:100%;}
+body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
+a{color:#1b61d6;text-decoration:none;}
+a:hover{color:#e88f00;text-decoration:underline;}
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
+#wrap {min-height: 100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
+#header{background-color:#e88f00;top:0;font-size:14px;}
+#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
+.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+.wrapper{width:100%}
+#top,#bottom{width:100%;}
+#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
+#bottom{color:#222;background-color:#ffffff;overflow:auto;padding-bottom:80px;}
+.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
+.top{padding-top:100px;}
+.app-welcome{margin-top:25px;}
+.app-name{color:#000000;font-weight:bold;}
+.bottom{padding-top:50px;}
+#left{width:325px;float:left;padding-right:25px;}
+#right{width:325px;float:right;padding-left:25px;}
+.align-left{text-align:left;}
+.align-right{text-align:right;}
+.align-center{text-align:center;}
+ul.links{margin:0;padding:0;}
+ul.links li{list-style-type:none;font-size:14px;}
+form{border-style:none;}
+fieldset{border-style:none;}
+input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
+input[type=text]{}
+input[type=submit]{background-color:#ddd;font-weight:bold;}
+/*Opera Fix*/
+body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/quick_tour/awesome/awesome/templates/mytemplate.jinja2 b/docs/quick_tour/awesome/awesome/templates/mytemplate.jinja2
new file mode 100644
index 000000000..8bf676041
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/templates/mytemplate.jinja2
@@ -0,0 +1,87 @@
+<!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" lang="en">
+<head>
+ <title>The Pyramid Web Framework</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.application_url}}/static/favicon.ico" />
+ <link rel="stylesheet" href="{{request.application_url}}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if !IE 7]>
+ <style type="text/css">
+ #wrap {display:table;height:100%}
+ </style>
+ <![endif]-->
+ <style type="text/css">
+ .locale { text-align: center; }
+ .locale-name { font-weight: bold; }
+ </style>
+</head>
+<body>
+ <div id="wrap">
+ <div id="header">
+ <div class="header">The Pyramid Web Framework</div>
+ </div>
+ <div id="top">
+ <div class="top align-center">
+ <img src="{{request.application_url}}/static/logo.png" width="300" height="80" alt="Logo"/>
+ <p class="app-welcome">
+ Welcome to <span class="app-name">{{project}}</span>, an application generated by<br/>
+ the Pyramid web framework.
+ </p>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="locale">
+ <h2>{% trans %}Hello!{% endtrans %}</h2>
+ <p>Request performed with <span class="locale-name">{{ request.locale_name }}</span> locale.</p>
+ </div>
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h3>Search Pyramid documentation</h3>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Search" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h3>Pyramid links</h3>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ </div>
+</body>
+</html>
diff --git a/docs/quick_tour/awesome/awesome/tests.py b/docs/quick_tour/awesome/awesome/tests.py
new file mode 100644
index 000000000..ac222e25b
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/tests.py
@@ -0,0 +1,21 @@
+import unittest
+from pyramid import testing
+from pyramid.i18n import TranslationStringFactory
+
+_ = TranslationStringFactory('awesome')
+
+
+class ViewTests(unittest.TestCase):
+
+ def setUp(self):
+ testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_my_view(self):
+ from awesome.views import my_view
+ request = testing.DummyRequest()
+ response = my_view(request)
+ self.assertEqual(response['project'], 'awesome')
+
diff --git a/docs/quick_tour/awesome/awesome/views.py b/docs/quick_tour/awesome/awesome/views.py
new file mode 100644
index 000000000..67b282f87
--- /dev/null
+++ b/docs/quick_tour/awesome/awesome/views.py
@@ -0,0 +1,6 @@
+from pyramid.i18n import TranslationStringFactory
+
+_ = TranslationStringFactory('awesome')
+
+def my_view(request):
+ return {'project':'awesome'}
diff --git a/docs/quick_tour/awesome/development.ini b/docs/quick_tour/awesome/development.ini
new file mode 100644
index 000000000..a473d32f1
--- /dev/null
+++ b/docs/quick_tour/awesome/development.ini
@@ -0,0 +1,49 @@
+[app:awesome]
+use = egg:awesome
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+debug_routematch = false
+debug_templates = true
+default_locale_name = en
+jinja2.directories = awesome:templates
+
+[pipeline:main]
+pipeline =
+ awesome
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, awesome
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_awesome]
+level = DEBUG
+handlers =
+qualname = awesome
+
+[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
diff --git a/docs/quick_tour/awesome/message-extraction.ini b/docs/quick_tour/awesome/message-extraction.ini
new file mode 100644
index 000000000..0c3d54bc1
--- /dev/null
+++ b/docs/quick_tour/awesome/message-extraction.ini
@@ -0,0 +1,3 @@
+[python: **.py]
+[jinja2: **.jinja2]
+encoding = utf-8
diff --git a/docs/quick_tour/awesome/setup.cfg b/docs/quick_tour/awesome/setup.cfg
new file mode 100644
index 000000000..b1cd90d2c
--- /dev/null
+++ b/docs/quick_tour/awesome/setup.cfg
@@ -0,0 +1,28 @@
+[nosetests]
+match = ^test
+nocapture = 1
+cover-package = awesome
+with-coverage = 1
+cover-erase = 1
+
+[compile_catalog]
+directory = awesome/locale
+domain = awesome
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = awesome/locale/awesome.pot
+width = 80
+mapping_file = message-extraction.ini
+
+[init_catalog]
+domain = awesome
+input_file = awesome/locale/awesome.pot
+output_dir = awesome/locale
+
+[update_catalog]
+domain = awesome
+input_file = awesome/locale/awesome.pot
+output_dir = awesome/locale
+previous = true
diff --git a/docs/quick_tour/awesome/setup.py b/docs/quick_tour/awesome/setup.py
new file mode 100644
index 000000000..32d666317
--- /dev/null
+++ b/docs/quick_tour/awesome/setup.py
@@ -0,0 +1,36 @@
+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>=1.0.2', 'pyramid_jinja2']
+
+setup(name='awesome',
+ version='0.0',
+ description='awesome',
+ 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="awesome",
+ entry_points = """\
+ [paste.app_factory]
+ main = awesome:main
+ """,
+ paster_plugins=['pyramid'],
+ )
diff --git a/docs/quick_tour/hello_world/app.py b/docs/quick_tour/hello_world/app.py
new file mode 100644
index 000000000..df5a6cf18
--- /dev/null
+++ b/docs/quick_tour/hello_world/app.py
@@ -0,0 +1,16 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ return Response('<h1>Hello World!</h1>')
+
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/jinja2/app.py b/docs/quick_tour/jinja2/app.py
new file mode 100644
index 000000000..83af219db
--- /dev/null
+++ b/docs/quick_tour/jinja2/app.py
@@ -0,0 +1,11 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/howdy/{name}')
+ config.include('pyramid_jinja2')
+ config.scan('views')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/jinja2/hello_world.jinja2 b/docs/quick_tour/jinja2/hello_world.jinja2
new file mode 100644
index 000000000..7a902dd3a
--- /dev/null
+++ b/docs/quick_tour/jinja2/hello_world.jinja2
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Hello World</title>
+</head>
+<body>
+<h1>Hello {{ name }}!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/jinja2/views.py b/docs/quick_tour/jinja2/views.py
new file mode 100644
index 000000000..916cdc720
--- /dev/null
+++ b/docs/quick_tour/jinja2/views.py
@@ -0,0 +1,8 @@
+from pyramid.view import view_config
+
+
+# Start View 1
+@view_config(route_name='hello', renderer='hello_world.jinja2')
+# End View 1
+def hello_world(request):
+ return dict(name=request.matchdict['name'])
diff --git a/docs/quick_tour/json/app.py b/docs/quick_tour/json/app.py
new file mode 100644
index 000000000..950cb478f
--- /dev/null
+++ b/docs/quick_tour/json/app.py
@@ -0,0 +1,15 @@
+from wsgiref.simple_server import make_server
+
+from pyramid.config import Configurator
+
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/howdy/{name}')
+ config.add_route('hello_json', 'hello.json')
+ config.add_static_view(name='static', path='static')
+ config.include('pyramid_jinja2')
+ config.scan('views')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/json/hello_world.jinja2 b/docs/quick_tour/json/hello_world.jinja2
new file mode 100644
index 000000000..f6862e618
--- /dev/null
+++ b/docs/quick_tour/json/hello_world.jinja2
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Glance</title>
+ <link rel="stylesheet" href="/static/app.css"/>
+</head>
+<body>
+<h1>Hello {{ name }}!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/json/hello_world.pt b/docs/quick_tour/json/hello_world.pt
new file mode 100644
index 000000000..711054aa9
--- /dev/null
+++ b/docs/quick_tour/json/hello_world.pt
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Glance</title>
+ <!-- Start Link 1 -->
+ <link rel="stylesheet" href="/static/app.css" />
+ <!-- End Link 1 -->
+ <!-- Start Link 2 -->
+ <link rel="stylesheet"
+ href="${request.static_url('static/app.css')}"
+ />
+ <!-- End Link 2 -->
+</head>
+<body>
+<h1>Hello ${name}!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/json/views.py b/docs/quick_tour/json/views.py
new file mode 100644
index 000000000..583e220c7
--- /dev/null
+++ b/docs/quick_tour/json/views.py
@@ -0,0 +1,13 @@
+from pyramid.view import view_config
+
+
+@view_config(route_name='hello', renderer='hello_world.pt')
+def hello_world(request):
+ return dict(name=request.matchdict['name'])
+
+
+# Start View 1
+@view_config(route_name='hello_json', renderer='json')
+def hello_json(request):
+ return [1, 2, 3]
+ # End View 1 \ No newline at end of file
diff --git a/docs/quick_tour/package/CHANGES.txt b/docs/quick_tour/package/CHANGES.txt
new file mode 100644
index 000000000..ffa255da8
--- /dev/null
+++ b/docs/quick_tour/package/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/quick_tour/package/MANIFEST.in b/docs/quick_tour/package/MANIFEST.in
new file mode 100644
index 000000000..18fbd855c
--- /dev/null
+++ b/docs/quick_tour/package/MANIFEST.in
@@ -0,0 +1,2 @@
+include *.txt *.ini *.cfg *.rst
+recursive-include hello_world *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/quick_tour/package/README.txt b/docs/quick_tour/package/README.txt
new file mode 100644
index 000000000..63aaf6fbd
--- /dev/null
+++ b/docs/quick_tour/package/README.txt
@@ -0,0 +1,4 @@
+hello_world README
+
+
+
diff --git a/docs/quick_tour/package/development.ini b/docs/quick_tour/package/development.ini
new file mode 100644
index 000000000..a3a73e885
--- /dev/null
+++ b/docs/quick_tour/package/development.ini
@@ -0,0 +1,54 @@
+# Start Includes
+[app:hello_world]
+pyramid.includes = pyramid_debugtoolbar
+# End Includes
+use = egg:hello_world
+reload_templates = true
+debug_authorization = false
+debug_notfound = false
+debug_routematch = false
+debug_templates = true
+default_locale_name = en
+jinja2.directories = hello_world:templates
+
+[pipeline:main]
+pipeline =
+ hello_world
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+# Start Sphinx Include
+[loggers]
+keys = root, hello_world
+
+[logger_hello_world]
+level = DEBUG
+handlers =
+qualname = hello_world
+# End Sphinx Include
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tour/package/hello_world/__init__.py b/docs/quick_tour/package/hello_world/__init__.py
new file mode 100644
index 000000000..6e66bf40a
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/__init__.py
@@ -0,0 +1,34 @@
+from pyramid.config import Configurator
+from pyramid_jinja2 import renderer_factory
+# Start Sphinx Include 1
+from pyramid.session import UnencryptedCookieSessionFactoryConfig
+# End Sphinx Include 1
+
+from hello_world.models import get_root
+
+def main(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ settings = dict(settings)
+ settings.setdefault('jinja2.i18n.domain', 'hello_world')
+
+ # Start Sphinx Include 2
+ my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')
+ config = Configurator(root_factory=get_root, settings=settings,
+ session_factory=my_session_factory)
+ # End Sphinx Include 2
+ config.add_translation_dirs('locale/')
+ # Start Include
+ config.include('pyramid_jinja2')
+ # End Include
+
+
+ config.add_static_view('static', 'static')
+ config.add_view('hello_world.views.my_view',
+ context='hello_world.models.MyModel',
+ renderer="mytemplate.jinja2")
+
+ return config.make_wsgi_app()
diff --git a/docs/quick_tour/package/hello_world/init.py b/docs/quick_tour/package/hello_world/init.py
new file mode 100644
index 000000000..9d7ec43d8
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/init.py
@@ -0,0 +1,34 @@
+from pyramid.config import Configurator
+from pyramid_jinja2 import renderer_factory
+# Start Sphinx 1
+from pyramid.session import UnencryptedCookieSessionFactoryConfig
+# End Sphinx 1
+
+from hello_world.models import get_root
+
+def main(global_config, **settings):
+ """ This function returns a WSGI application.
+
+ It is usually called by the PasteDeploy framework during
+ ``paster serve``.
+ """
+ settings = dict(settings)
+ settings.setdefault('jinja2.i18n.domain', 'hello_world')
+
+ config = Configurator(root_factory=get_root, settings=settings)
+ config.add_translation_dirs('locale/')
+ # Start Include
+ config.include('pyramid_jinja2')
+ # End Include
+
+ # Start Sphinx Include 2
+ my_session_factory = UnencryptedCookieSessionFactoryConfig('itsaseekreet')
+ config = Configurator(session_factory=my_session_factory)
+ # End Sphinx Include 2
+
+ config.add_static_view('static', 'static')
+ config.add_view('hello_world.views.my_view',
+ context='hello_world.models.MyModel',
+ renderer="mytemplate.jinja2")
+
+ return config.make_wsgi_app()
diff --git a/docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.mo b/docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.mo
new file mode 100644
index 000000000..40bf0c271
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.mo
Binary files differ
diff --git a/docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.po b/docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.po
new file mode 100644
index 000000000..0df243dba
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/locale/de/LC_MESSAGES/hello_world.po
@@ -0,0 +1,21 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-05-12 09:14-0330\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+msgid "Hello!"
+msgstr "Hallo!"
diff --git a/docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.mo b/docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.mo
new file mode 100644
index 000000000..4fc438bfe
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.mo
Binary files differ
diff --git a/docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.po b/docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.po
new file mode 100644
index 000000000..dc0aae5d7
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/locale/fr/LC_MESSAGES/hello_world.po
@@ -0,0 +1,21 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-05-12 09:14-0330\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+msgid "Hello!"
+msgstr "Bonjour!"
diff --git a/docs/quick_tour/package/hello_world/locale/hello_world.pot b/docs/quick_tour/package/hello_world/locale/hello_world.pot
new file mode 100644
index 000000000..9c9460cb2
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/locale/hello_world.pot
@@ -0,0 +1,21 @@
+# Translations template for PROJECT.
+# Copyright (C) 2011 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PROJECT VERSION\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2011-05-12 09:14-0330\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.6\n"
+
+msgid "Hello!"
+msgstr ""
diff --git a/docs/quick_tour/package/hello_world/models.py b/docs/quick_tour/package/hello_world/models.py
new file mode 100644
index 000000000..edd361c9c
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/models.py
@@ -0,0 +1,8 @@
+class MyModel(object):
+ pass
+
+root = MyModel()
+
+
+def get_root(request):
+ return root
diff --git a/docs/quick_tour/package/hello_world/static/favicon.ico b/docs/quick_tour/package/hello_world/static/favicon.ico
new file mode 100644
index 000000000..71f837c9e
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/static/favicon.ico
Binary files differ
diff --git a/docs/quick_tour/package/hello_world/static/logo.png b/docs/quick_tour/package/hello_world/static/logo.png
new file mode 100644
index 000000000..88f5d9865
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/static/logo.png
Binary files differ
diff --git a/docs/quick_tour/package/hello_world/static/pylons.css b/docs/quick_tour/package/hello_world/static/pylons.css
new file mode 100644
index 000000000..42e2e320e
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/static/pylons.css
@@ -0,0 +1,73 @@
+html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;/* 16px */
+vertical-align:baseline;background:transparent;}
+body{line-height:1;}
+ol,ul{list-style:none;}
+blockquote,q{quotes:none;}
+blockquote:before,blockquote:after,q:before,q:after{content:'';content:none;}
+/* remember to define focus styles! */
+:focus{outline:0;}
+/* remember to highlight inserts somehow! */
+ins{text-decoration:none;}
+del{text-decoration:line-through;}
+/* tables still need 'cellspacing="0"' in the markup */
+table{border-collapse:collapse;border-spacing:0;}
+/* restyling */
+sub{vertical-align:sub;font-size:smaller;line-height:normal;}
+sup{vertical-align:super;font-size:smaller;line-height:normal;}
+/* lists */
+ul,menu,dir{display:block;list-style-type:disc;margin:1em 0;padding-left:40px;}
+ol{display:block;list-style-type:decimal-leading-zero;margin:1em 0;padding-left:40px;}
+li{display:list-item;}
+/* nested lists have no top/bottom margins */
+ul ul,ul ol,ul dir,ul menu,ul dl,ol ul,ol ol,ol dir,ol menu,ol dl,dir ul,dir ol,dir dir,dir menu,dir dl,menu ul,menu ol,menu dir,menu menu,menu dl,dl ul,dl ol,dl dir,dl menu,dl dl{margin-top:0;margin-bottom:0;}
+/* 2 deep unordered lists use a circle */
+ol ul,ul ul,menu ul,dir ul,ol menu,ul menu,menu menu,dir menu,ol dir,ul dir,menu dir,dir dir{list-style-type:circle;}
+/* 3 deep (or more) unordered lists use a square */
+ol ol ul,ol ul ul,ol menu ul,ol dir ul,ol ol menu,ol ul menu,ol menu menu,ol dir menu,ol ol dir,ol ul dir,ol menu dir,ol dir dir,ul ol ul,ul ul ul,ul menu ul,ul dir ul,ul ol menu,ul ul menu,ul menu menu,ul dir menu,ul ol dir,ul ul dir,ul menu dir,ul dir dir,menu ol ul,menu ul ul,menu menu ul,menu dir ul,menu ol menu,menu ul menu,menu menu menu,menu dir menu,menu ol dir,menu ul dir,menu menu dir,menu dir dir,dir ol ul,dir ul ul,dir menu ul,dir dir ul,dir ol menu,dir ul menu,dir menu menu,dir dir menu,dir ol dir,dir ul dir,dir menu dir,dir dir dir{list-style-type:square;}
+.hidden{display:none;}
+p{line-height:1.5em;}
+h1{font-size:1.75em;/* 28px */
+line-height:1.7em;font-family:helvetica,verdana;}
+h2{font-size:1.5em;/* 24px */
+line-height:1.7em;font-family:helvetica,verdana;}
+h3{font-size:1.25em;/* 20px */
+line-height:1.7em;font-family:helvetica,verdana;}
+h4{font-size:1em;line-height:1.7em;font-family:helvetica,verdana;}
+html,body{width:100%;height:100%;}
+body{margin:0;padding:0;background-color:#ffffff;position:relative;font:16px/24px "Nobile","Lucida Grande",Lucida,Verdana,sans-serif;}
+a{color:#1b61d6;text-decoration:none;}
+a:hover{color:#e88f00;text-decoration:underline;}
+body h1,
+body h2,
+body h3,
+body h4,
+body h5,
+body h6{font-family:"Nobile","Lucida Grande",Lucida,Verdana,sans-serif;font-weight:normal;color:#144fb2;font-style:normal;}
+#wrap {min-height: 100%;}
+#header,#footer{width:100%;color:#ffffff;height:40px;position:absolute;text-align:center;line-height:40px;overflow:hidden;font-size:12px;}
+#header{background-color:#e88f00;top:0;font-size:14px;}
+#footer{background-color:#000000;bottom:0;position: relative;margin-top:-40px;clear:both;}
+.header,.footer{width:700px;margin-right:auto;margin-left:auto;}
+.wrapper{width:100%}
+#top,#bottom{width:100%;}
+#top{color:#888;background-color:#eee;height:300px;border-bottom:2px solid #ddd;}
+#bottom{color:#222;background-color:#ffffff;overflow:auto;padding-bottom:80px;}
+.top,.bottom{width:700px;margin-right:auto;margin-left:auto;}
+.top{padding-top:100px;}
+.app-welcome{margin-top:25px;}
+.app-name{color:#000000;font-weight:bold;}
+.bottom{padding-top:50px;}
+#left{width:325px;float:left;padding-right:25px;}
+#right{width:325px;float:right;padding-left:25px;}
+.align-left{text-align:left;}
+.align-right{text-align:right;}
+.align-center{text-align:center;}
+ul.links{margin:0;padding:0;}
+ul.links li{list-style-type:none;font-size:14px;}
+form{border-style:none;}
+fieldset{border-style:none;}
+input{color:#222;border:1px solid #ccc;font-family:sans-serif;font-size:12px;line-height:16px;}
+input[type=text]{}
+input[type=submit]{background-color:#ddd;font-weight:bold;}
+/*Opera Fix*/
+body:before {content:"";height:100%;float:left;width:0;margin-top:-32767px;}
diff --git a/docs/quick_tour/package/hello_world/templates/mytemplate.jinja2 b/docs/quick_tour/package/hello_world/templates/mytemplate.jinja2
new file mode 100644
index 000000000..25a28ed7a
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/templates/mytemplate.jinja2
@@ -0,0 +1,90 @@
+<!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" lang="en">
+<head>
+ <title>The Pyramid Web Framework</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.application_url}}/static/favicon.ico" />
+ <link rel="stylesheet" href="{{request.application_url}}/static/pylons.css" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Nobile:regular,italic,bold,bolditalic&amp;subset=latin" type="text/css" media="screen" charset="utf-8" />
+ <!--[if !IE 7]>
+ <style type="text/css">
+ #wrap {display:table;height:100%}
+ </style>
+ <![endif]-->
+ <style type="text/css">
+ .locale { text-align: center; }
+ .locale-name { font-weight: bold; }
+ </style>
+</head>
+<body>
+ <div id="wrap">
+ <div id="header">
+ <div class="header">The Pyramid Web Framework</div>
+ </div>
+ <div id="top">
+ <div class="top align-center">
+ <img src="{{request.application_url}}/static/logo.png" width="300" height="80" alt="Logo"/>
+ <p class="app-welcome">
+ Welcome to <span class="app-name">{{project}}</span>, an application generated by<br/>
+ the Pyramid Web Framework.
+ </p>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="locale">
+ <h2>{% trans %}Hello!{% endtrans %}</h2>
+ <!-- Start Sphinx Include 1 -->
+ <p>Counter: {{ request.session.counter }}</p>
+ <!-- End Sphinx Include 1 -->
+ <p>Request performed with <span class="locale-name">{{ request.locale_name }}</span> locale.</p>
+ </div>
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h3>Search Pyramid documentation</h3>
+ <form method="get" action="http://docs.pylonshq.com/pyramid/dev/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Search" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h3>Pyramid links</h3>
+ <ul class="links">
+ <li>
+ <a href="http://pylonshq.com">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/">The Pylons Project Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#api-documentation">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonshq.com/pyramid/dev/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">© Copyright 2008-2010, Agendaless Consulting.</div>
+ </div>
+</body>
+</html>
diff --git a/docs/quick_tour/package/hello_world/tests.py b/docs/quick_tour/package/hello_world/tests.py
new file mode 100644
index 000000000..ccec14f70
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/tests.py
@@ -0,0 +1,20 @@
+import unittest
+from pyramid import testing
+from pyramid.i18n import TranslationStringFactory
+
+_ = TranslationStringFactory('hello_world')
+
+
+class ViewTests(unittest.TestCase):
+
+ def setUp(self):
+ testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_my_view(self):
+ from hello_world.views import my_view
+ request = testing.DummyRequest()
+ response = my_view(request)
+ self.assertEqual(response['project'], 'hello_world')
diff --git a/docs/quick_tour/package/hello_world/views.py b/docs/quick_tour/package/hello_world/views.py
new file mode 100644
index 000000000..109c260ad
--- /dev/null
+++ b/docs/quick_tour/package/hello_world/views.py
@@ -0,0 +1,22 @@
+# Start Logging 1
+import logging
+log = logging.getLogger(__name__)
+# End Logging 1
+
+from pyramid.i18n import TranslationStringFactory
+
+_ = TranslationStringFactory('hello_world')
+
+
+def my_view(request):
+ # Start Logging 2
+ log.debug('Some Message')
+ # End Logging 2
+ # Start Sphinx Include 1
+ session = request.session
+ if 'counter' in session:
+ session['counter'] += 1
+ else:
+ session['counter'] = 0
+ # End Sphinx Include 1
+ return {'project': 'hello_world'}
diff --git a/docs/quick_tour/package/message-extraction.ini b/docs/quick_tour/package/message-extraction.ini
new file mode 100644
index 000000000..0c3d54bc1
--- /dev/null
+++ b/docs/quick_tour/package/message-extraction.ini
@@ -0,0 +1,3 @@
+[python: **.py]
+[jinja2: **.jinja2]
+encoding = utf-8
diff --git a/docs/quick_tour/package/setup.cfg b/docs/quick_tour/package/setup.cfg
new file mode 100644
index 000000000..186e796fc
--- /dev/null
+++ b/docs/quick_tour/package/setup.cfg
@@ -0,0 +1,28 @@
+[nosetests]
+match = ^test
+nocapture = 1
+cover-package = hello_world
+with-coverage = 1
+cover-erase = 1
+
+[compile_catalog]
+directory = hello_world/locale
+domain = hello_world
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = hello_world/locale/hello_world.pot
+width = 80
+mapping_file = message-extraction.ini
+
+[init_catalog]
+domain = hello_world
+input_file = hello_world/locale/hello_world.pot
+output_dir = hello_world/locale
+
+[update_catalog]
+domain = hello_world
+input_file = hello_world/locale/hello_world.pot
+output_dir = hello_world/locale
+previous = true
diff --git a/docs/quick_tour/package/setup.py b/docs/quick_tour/package/setup.py
new file mode 100644
index 000000000..f118ed5fb
--- /dev/null
+++ b/docs/quick_tour/package/setup.py
@@ -0,0 +1,41 @@
+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()
+
+# Start Requires
+requires = ['pyramid>=1.0.2', 'pyramid_jinja2', 'pyramid_debugtoolbar']
+# End Requires
+
+setup(name='hello_world',
+ version='0.0',
+ description='hello_world',
+ 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="hello_world",
+ entry_points="""\
+ [paste.app_factory]
+ main = hello_world:main
+ """,
+ paster_plugins=['pyramid'],
+ extras_require={
+ 'testing': ['nose', ],
+ }
+) \ No newline at end of file
diff --git a/docs/quick_tour/requests/app.py b/docs/quick_tour/requests/app.py
new file mode 100644
index 000000000..7ac81eb50
--- /dev/null
+++ b/docs/quick_tour/requests/app.py
@@ -0,0 +1,24 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ # Some parameters from a request such as /?name=lisa
+ url = request.url
+ name = request.params.get('name', 'No Name Provided')
+
+ body = 'URL %s with name: %s' % (url, name)
+ return Response(
+ content_type="text/plain",
+ body=body
+ )
+
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/routing/app.py b/docs/quick_tour/routing/app.py
new file mode 100644
index 000000000..04a8a6344
--- /dev/null
+++ b/docs/quick_tour/routing/app.py
@@ -0,0 +1,12 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+
+if __name__ == '__main__':
+ config = Configurator()
+ # Start Route 1
+ config.add_route('hello', '/howdy/{first}/{last}')
+ # End Route 1
+ config.scan('views')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/routing/views.py b/docs/quick_tour/routing/views.py
new file mode 100644
index 000000000..8cb8d3780
--- /dev/null
+++ b/docs/quick_tour/routing/views.py
@@ -0,0 +1,10 @@
+from pyramid.response import Response
+from pyramid.view import view_config
+
+
+# Start Route 1
+@view_config(route_name='hello')
+def hello_world(request):
+ body = '<h1>Hi %(first)s %(last)s!</h1>' % request.matchdict
+ return Response(body)
+ # End Route 1 \ No newline at end of file
diff --git a/docs/quick_tour/sqla_demo/CHANGES.txt b/docs/quick_tour/sqla_demo/CHANGES.txt
new file mode 100644
index 000000000..35a34f332
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/quick_tour/sqla_demo/MANIFEST.in b/docs/quick_tour/sqla_demo/MANIFEST.in
new file mode 100644
index 000000000..a432577e9
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/MANIFEST.in
@@ -0,0 +1,2 @@
+include *.txt *.ini *.cfg *.rst
+recursive-include sqla_demo *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/quick_tour/sqla_demo/README.txt b/docs/quick_tour/sqla_demo/README.txt
new file mode 100644
index 000000000..f35d3aec5
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/README.txt
@@ -0,0 +1,14 @@
+sqla_demo README
+==================
+
+Getting Started
+---------------
+
+- cd <directory containing this file>
+
+- $venv/bin/python setup.py develop
+
+- $venv/bin/initialize_sqla_demo_db development.ini
+
+- $venv/bin/pserve development.ini
+
diff --git a/docs/quick_tour/sqla_demo/development.ini b/docs/quick_tour/sqla_demo/development.ini
new file mode 100644
index 000000000..174468abf
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/development.ini
@@ -0,0 +1,71 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
+[app:main]
+use = egg:sqla_demo
+
+pyramid.reload_templates = true
+pyramid.debug_authorization = false
+pyramid.debug_notfound = false
+pyramid.debug_routematch = false
+pyramid.default_locale_name = en
+pyramid.includes =
+ pyramid_debugtoolbar
+ pyramid_tm
+
+sqlalchemy.url = sqlite:///%(here)s/sqla_demo.sqlite
+
+# 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, sqla_demo, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_sqla_demo]
+level = DEBUG
+handlers =
+qualname = sqla_demo
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[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
diff --git a/docs/quick_tour/sqla_demo/production.ini b/docs/quick_tour/sqla_demo/production.ini
new file mode 100644
index 000000000..dc0ba304f
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/production.ini
@@ -0,0 +1,62 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
+[app:main]
+use = egg:sqla_demo
+
+pyramid.reload_templates = false
+pyramid.debug_authorization = false
+pyramid.debug_notfound = false
+pyramid.debug_routematch = false
+pyramid.default_locale_name = en
+pyramid.includes =
+ pyramid_tm
+
+sqlalchemy.url = sqlite:///%(here)s/sqla_demo.sqlite
+
+[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, sqla_demo, sqlalchemy
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+
+[logger_sqla_demo]
+level = WARN
+handlers =
+qualname = sqla_demo
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+# "level = INFO" logs SQL queries.
+# "level = DEBUG" logs SQL queries and results.
+# "level = WARN" logs neither. (Recommended for production systems.)
+
+[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
diff --git a/docs/quick_tour/sqla_demo/setup.cfg b/docs/quick_tour/sqla_demo/setup.cfg
new file mode 100644
index 000000000..9f91cd122
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/setup.cfg
@@ -0,0 +1,27 @@
+[nosetests]
+match=^test
+nocapture=1
+cover-package=sqla_demo
+with-coverage=1
+cover-erase=1
+
+[compile_catalog]
+directory = sqla_demo/locale
+domain = sqla_demo
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = sqla_demo/locale/sqla_demo.pot
+width = 80
+
+[init_catalog]
+domain = sqla_demo
+input_file = sqla_demo/locale/sqla_demo.pot
+output_dir = sqla_demo/locale
+
+[update_catalog]
+domain = sqla_demo
+input_file = sqla_demo/locale/sqla_demo.pot
+output_dir = sqla_demo/locale
+previous = true
diff --git a/docs/quick_tour/sqla_demo/setup.py b/docs/quick_tour/sqla_demo/setup.py
new file mode 100644
index 000000000..ac2eed035
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/setup.py
@@ -0,0 +1,44 @@
+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',
+ ]
+
+setup(name='sqla_demo',
+ version='0.0',
+ description='sqla_demo',
+ 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='sqla_demo',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = sqla_demo:main
+ [console_scripts]
+ initialize_sqla_demo_db = sqla_demo.scripts.initializedb:main
+ """,
+ )
diff --git a/docs/quick_tour/sqla_demo/sqla_demo.sqlite b/docs/quick_tour/sqla_demo/sqla_demo.sqlite
new file mode 100644
index 000000000..fa6adb104
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo.sqlite
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/__init__.py b/docs/quick_tour/sqla_demo/sqla_demo/__init__.py
new file mode 100644
index 000000000..aac7c5e69
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/__init__.py
@@ -0,0 +1,20 @@
+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()
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/models.py b/docs/quick_tour/sqla_demo/sqla_demo/models.py
new file mode 100644
index 000000000..3dfb40e58
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/models.py
@@ -0,0 +1,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()
+
+# Start Sphinx Include
+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
+ # End Sphinx Include
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/scripts/__init__.py b/docs/quick_tour/sqla_demo/sqla_demo/scripts/__init__.py
new file mode 100644
index 000000000..5bb534f79
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/scripts/__init__.py
@@ -0,0 +1 @@
+# package
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/scripts/initializedb.py b/docs/quick_tour/sqla_demo/sqla_demo/scripts/initializedb.py
new file mode 100644
index 000000000..66feb3008
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/scripts/initializedb.py
@@ -0,0 +1,37 @@
+import os
+import sys
+import transaction
+
+from sqlalchemy import engine_from_config
+
+from pyramid.paster import (
+ get_appsettings,
+ setup_logging,
+ )
+
+from ..models import (
+ DBSession,
+ MyModel,
+ Base,
+ )
+
+
+def usage(argv):
+ 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 = MyModel(name='one', value=1)
+ DBSession.add(model)
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/favicon.ico b/docs/quick_tour/sqla_demo/sqla_demo/static/favicon.ico
new file mode 100644
index 000000000..71f837c9e
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/favicon.ico
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/footerbg.png b/docs/quick_tour/sqla_demo/sqla_demo/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/footerbg.png
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/headerbg.png b/docs/quick_tour/sqla_demo/sqla_demo/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/headerbg.png
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/ie6.css b/docs/quick_tour/sqla_demo/sqla_demo/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/middlebg.png b/docs/quick_tour/sqla_demo/sqla_demo/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/middlebg.png
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/pylons.css b/docs/quick_tour/sqla_demo/sqla_demo/static/pylons.css
new file mode 100644
index 000000000..4b1c017cd
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/pylons.css
@@ -0,0 +1,372 @@
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
+/*Opera Fix*/
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/pyramid-small.png b/docs/quick_tour/sqla_demo/sqla_demo/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/pyramid-small.png
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/pyramid.png b/docs/quick_tour/sqla_demo/sqla_demo/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/pyramid.png
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/static/transparent.gif b/docs/quick_tour/sqla_demo/sqla_demo/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/static/transparent.gif
Binary files differ
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/templates/mytemplate.pt b/docs/quick_tour/sqla_demo/sqla_demo/templates/mytemplate.pt
new file mode 100644
index 000000000..321c0f5fb
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/templates/mytemplate.pt
@@ -0,0 +1,76 @@
+<!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>The Pyramid Web Framework</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('sqla_demo:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('sqla_demo:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('sqla_demo:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
+</head>
+<body>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('sqla_demo:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid web framework.
+ </p>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonsproject.org">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#reference-material">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#detailed-change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="footer">
+ <div class="footer">&copy; Copyright 2008-2012, Agendaless Consulting.</div>
+ </div>
+</body>
+</html>
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/tests.py b/docs/quick_tour/sqla_demo/sqla_demo/tests.py
new file mode 100644
index 000000000..6fef6d695
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/tests.py
@@ -0,0 +1,33 @@
+import unittest
+import transaction
+
+from pyramid import testing
+
+from .models import DBSession
+
+
+class TestMyView(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+ from sqlalchemy import create_engine
+ engine = create_engine('sqlite://')
+ from .models import (
+ Base,
+ MyModel,
+ )
+ DBSession.configure(bind=engine)
+ Base.metadata.create_all(engine)
+ with transaction.manager:
+ model = MyModel(name='one', value=55)
+ DBSession.add(model)
+
+ def tearDown(self):
+ DBSession.remove()
+ testing.tearDown()
+
+ def test_it(self):
+ from .views import my_view
+ request = testing.DummyRequest()
+ info = my_view(request)
+ self.assertEqual(info['one'].name, 'one')
+ self.assertEqual(info['project'], 'sqla_demo')
diff --git a/docs/quick_tour/sqla_demo/sqla_demo/views.py b/docs/quick_tour/sqla_demo/sqla_demo/views.py
new file mode 100644
index 000000000..768a7e42e
--- /dev/null
+++ b/docs/quick_tour/sqla_demo/sqla_demo/views.py
@@ -0,0 +1,37 @@
+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:
+ # Start Sphinx Include
+ one = DBSession.query(MyModel).filter(MyModel.name == 'one').first()
+ # End Sphinx Include
+ except DBAPIError:
+ return Response(conn_err_msg, content_type='text/plain', status_int=500)
+ return {'one': one, 'project': 'sqla_demo'}
+
+conn_err_msg = """\
+Pyramid is having a problem using your SQL database. The problem
+might be caused by one of the following things:
+
+1. You may need to run the "initialize_sqla_demo_db" script
+ to initialize your database tables. Check your virtual
+ environment's "bin" directory for this script and try to run it.
+
+2. 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.
+
+After you fix the problem, please restart the Pyramid application to
+try it again.
+"""
+
diff --git a/docs/quick_tour/static_assets/app.py b/docs/quick_tour/static_assets/app.py
new file mode 100644
index 000000000..9c808972f
--- /dev/null
+++ b/docs/quick_tour/static_assets/app.py
@@ -0,0 +1,14 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/howdy/{name}')
+ # Start Static 1
+ config.add_static_view(name='static', path='static')
+ # End Static 1
+ config.include('pyramid_jinja2')
+ config.scan('views')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/static_assets/hello_world.jinja2 b/docs/quick_tour/static_assets/hello_world.jinja2
new file mode 100644
index 000000000..f6862e618
--- /dev/null
+++ b/docs/quick_tour/static_assets/hello_world.jinja2
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Glance</title>
+ <link rel="stylesheet" href="/static/app.css"/>
+</head>
+<body>
+<h1>Hello {{ name }}!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/static_assets/hello_world.pt b/docs/quick_tour/static_assets/hello_world.pt
new file mode 100644
index 000000000..1797146eb
--- /dev/null
+++ b/docs/quick_tour/static_assets/hello_world.pt
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en"><head>
+ <title>Quick Glance</title>
+ <!-- Start Link 1 -->
+ <link rel="stylesheet" href="/static/app.css" />
+ <!-- End Link 1 -->
+ <!-- Start Link 2 -->
+ <link rel="stylesheet"
+ href="${request.static_url('static/app.css')}"
+ />
+ <!-- End Link 2 -->
+</head>
+<body>
+<h1>Hello ${name}!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/static_assets/static/app.css b/docs/quick_tour/static_assets/static/app.css
new file mode 100644
index 000000000..f8acf3164
--- /dev/null
+++ b/docs/quick_tour/static_assets/static/app.css
@@ -0,0 +1,4 @@
+body {
+ margin: 2em;
+ font-family: sans-serif;
+} \ No newline at end of file
diff --git a/docs/quick_tour/static_assets/views.py b/docs/quick_tour/static_assets/views.py
new file mode 100644
index 000000000..90730ae32
--- /dev/null
+++ b/docs/quick_tour/static_assets/views.py
@@ -0,0 +1,6 @@
+from pyramid.view import view_config
+
+
+@view_config(route_name='hello', renderer='hello_world.pt')
+def hello_world(request):
+ return dict(name=request.matchdict['name'])
diff --git a/docs/quick_tour/templating/app.py b/docs/quick_tour/templating/app.py
new file mode 100644
index 000000000..6d1a29f4e
--- /dev/null
+++ b/docs/quick_tour/templating/app.py
@@ -0,0 +1,10 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/howdy/{name}')
+ config.scan('views')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/templating/hello_world.pt b/docs/quick_tour/templating/hello_world.pt
new file mode 100644
index 000000000..ae14f447d
--- /dev/null
+++ b/docs/quick_tour/templating/hello_world.pt
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Glance</title>
+</head>
+<body>
+<h1>Hello ${name}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/templating/views.py b/docs/quick_tour/templating/views.py
new file mode 100644
index 000000000..6c7846efa
--- /dev/null
+++ b/docs/quick_tour/templating/views.py
@@ -0,0 +1,8 @@
+from pyramid.view import view_config
+
+
+# Start View 1
+@view_config(route_name='hello', renderer='hello_world.pt')
+def hello_world(request):
+ return dict(name=request.matchdict['name'])
+ # End View 1 \ No newline at end of file
diff --git a/docs/quick_tour/view_classes/app.py b/docs/quick_tour/view_classes/app.py
new file mode 100644
index 000000000..468c8c29e
--- /dev/null
+++ b/docs/quick_tour/view_classes/app.py
@@ -0,0 +1,13 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+
+if __name__ == '__main__':
+ config = Configurator()
+ # Start Routes 1
+ config.add_route('hello', '/howdy/{name}')
+ # End Routes 1
+ config.include('pyramid_jinja2')
+ config.scan('views')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/view_classes/delete.jinja2 b/docs/quick_tour/view_classes/delete.jinja2
new file mode 100644
index 000000000..ba45b7d16
--- /dev/null
+++ b/docs/quick_tour/view_classes/delete.jinja2
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Delete World</title>
+</head>
+<body>
+<h1>Delete {{ view.name }}!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/view_classes/edit.jinja2 b/docs/quick_tour/view_classes/edit.jinja2
new file mode 100644
index 000000000..ce0eb5bd1
--- /dev/null
+++ b/docs/quick_tour/view_classes/edit.jinja2
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Edit World</title>
+</head>
+<body>
+<h1>Edit {{ view.name }}!</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/view_classes/hello.jinja2 b/docs/quick_tour/view_classes/hello.jinja2
new file mode 100644
index 000000000..3446b96ce
--- /dev/null
+++ b/docs/quick_tour/view_classes/hello.jinja2
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Hello World</title>
+</head>
+<body>
+<h1>Hello {{ view.name }}!</h1>
+<!-- Start Form 1 -->
+<form method="POST"
+ action="{{ request.current_route_url() }}">
+ <input name="new_name"/>
+ <input type="submit" name="form.edit" value="Save"/>
+ <input type="submit" name="form.delete" value="Delete"/>
+</form>
+<!-- End Form 1 -->
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tour/view_classes/views.py b/docs/quick_tour/view_classes/views.py
new file mode 100644
index 000000000..62556142e
--- /dev/null
+++ b/docs/quick_tour/view_classes/views.py
@@ -0,0 +1,32 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+# Start View 1
+# One route, at /howdy/amy, so don't repeat on each @view_config
+@view_defaults(route_name='hello')
+class HelloWorldViews:
+ def __init__(self, request):
+ self.request = request
+ # Our templates can now say {{ view.name }}
+ self.name = request.matchdict['name']
+
+ # Retrieving /howdy/amy the first time
+ @view_config(renderer='hello.jinja2')
+ def hello_view(self):
+ return dict()
+
+ # Posting to /howdy/amy via the "Edit" submit button
+ @view_config(request_param='form.edit', renderer='edit.jinja2')
+ def edit_view(self):
+ print('Edited')
+ return dict()
+
+ # Posting to /howdy/amy via the "Delete" submit button
+ @view_config(request_param='form.delete', renderer='delete.jinja2')
+ def delete_view(self):
+ print('Deleted')
+ return dict()
+ # End View 1 \ No newline at end of file
diff --git a/docs/quick_tour/views/app.py b/docs/quick_tour/views/app.py
new file mode 100644
index 000000000..54dc9ed4b
--- /dev/null
+++ b/docs/quick_tour/views/app.py
@@ -0,0 +1,13 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.add_route('redirect', '/goto')
+ config.add_route('exception', '/problem')
+ config.scan('views')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tour/views/views.py b/docs/quick_tour/views/views.py
new file mode 100644
index 000000000..9dc795f14
--- /dev/null
+++ b/docs/quick_tour/views/views.py
@@ -0,0 +1,29 @@
+from pyramid.httpexceptions import HTTPFound
+from pyramid.response import Response
+from pyramid.view import view_config
+
+
+# First view, available at http://localhost:6543/
+@view_config(route_name='home')
+def home_view(request):
+ return Response('<p>Visit <a href="/howdy?name=lisa">hello</a></p>')
+
+
+# /howdy?name=alice which links to the next view
+@view_config(route_name='hello')
+def hello_view(request):
+ name = request.params.get('name', 'No Name')
+ body = '<p>Hi %s, this <a href="/goto">redirects</a></p>'
+ return Response(body % name)
+
+
+# /goto which issues HTTP redirect to the last view
+@view_config(route_name='redirect')
+def redirect_view(request):
+ return HTTPFound(location="/problem")
+
+
+# /problem which causes an site error
+@view_config(route_name='exception')
+def exception_view(request):
+ raise Exception()
diff --git a/docs/quick_tutorial/authentication.rst b/docs/quick_tutorial/authentication.rst
new file mode 100644
index 000000000..8380a75ed
--- /dev/null
+++ b/docs/quick_tutorial/authentication.rst
@@ -0,0 +1,134 @@
+==============================
+20: Logins With Authentication
+==============================
+
+Login views that authenticate a username/password against a list of
+users.
+
+Background
+==========
+
+Most web applications have URLs that allow people to add/edit/delete
+content via a web browser. Time to add
+:ref:`security <security_chapter>`
+to the application. In this first step we introduce authentication.
+That is, logging in and logging out using Pyramid's rich facilities for
+pluggable user storages.
+
+In the next step we will introduce protection resources with
+authorization security statements.
+
+Objectives
+==========
+
+- Introduce the Pyramid concepts of authentication
+
+- Create login/logout views
+
+Steps
+=====
+
+#. We are going to use the view classes step as our starting point:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes authentication; cd authentication
+ $ $VENV/bin/python setup.py develop
+
+#. Put the security hash in the ``authentication/development.ini``
+ configuration file as ``tutorial.secret`` instead of putting it in
+ the code:
+
+ .. literalinclude:: authentication/development.ini
+ :language: ini
+ :linenos:
+
+#. Get authentication (and for now, authorization policies) and login
+ route into the :term:`configurator` in
+ ``authentication/tutorial/__init__.py``:
+
+ .. literalinclude:: authentication/tutorial/__init__.py
+ :linenos:
+
+#. Create a ``authentication/tutorial/security.py`` module that can find
+ our user information by providing an *authentication policy callback*:
+
+ .. literalinclude:: authentication/tutorial/security.py
+ :linenos:
+
+#. Update the views in ``authentication/tutorial/views.py``:
+
+ .. literalinclude:: authentication/tutorial/views.py
+ :linenos:
+
+#. Add a login template at ``authentication/tutorial/login.pt``:
+
+ .. literalinclude:: authentication/tutorial/login.pt
+ :language: html
+ :linenos:
+
+#. Provide a login/logout box in ``authentication/tutorial/home.pt``
+
+ .. literalinclude:: authentication/tutorial/home.pt
+ :language: html
+ :linenos:
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in a browser.
+
+#. Click the "Log In" link.
+
+#. Submit the login form with the username ``editor`` and the password
+ ``editor``.
+
+#. Note that the "Log In" link has changed to "Logout".
+
+#. Click the "Logout" link.
+
+Analysis
+========
+
+Unlike many web frameworks, Pyramid includes a built-in (but optional)
+security model for authentication and authorization. This security
+system is intended to be flexible and support many needs. In this
+security model, authentication (who are you) and authorization (what
+are you allowed to do) are not just pluggable, but de-coupled. To learn
+one step at a time, we provide a system that identifies users and lets
+them log out.
+
+In this example we chose to use the bundled
+:ref:`AuthTktAuthenticationPolicy <authentication_module>`
+policy. We enabled it in our configuration and provided a
+ticket-signing secret in our INI file.
+
+Our view class grew a login view. When you reached it via a GET,
+it returned a login form. When reached via POST, it processed the
+username and password against the "groupfinder" callable that we
+registered in the configuration.
+
+In our template, we fetched the ``logged_in`` value from the view
+class. We use this to calculate the logged-in user,
+if any. In the template we can then choose to show a login link to
+anonymous visitors or a logout link to logged-in users.
+
+Extra Credit
+============
+
+#. What is the difference between a user and a principal?
+
+#. Can I use a database behind my ``groupfinder`` to look up principals?
+
+#. Do I have to put a ``renderer`` in my ``@forbidden_view_config``
+ decorator?
+
+#. Once I am logged in, does any user-centric information get jammed
+ onto each request? Use ``import pdb; pdb.set_trace()`` to answer
+ this.
+
+.. seealso:: :ref:`security_chapter`,
+ :ref:`AuthTktAuthenticationPolicy <authentication_module>`
diff --git a/docs/quick_tutorial/authentication/development.ini b/docs/quick_tutorial/authentication/development.ini
new file mode 100644
index 000000000..5d4580ff5
--- /dev/null
+++ b/docs/quick_tutorial/authentication/development.ini
@@ -0,0 +1,42 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+tutorial.secret = 98zd
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/authentication/setup.py b/docs/quick_tutorial/authentication/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/authentication/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/authentication/tutorial/__init__.py b/docs/quick_tutorial/authentication/tutorial/__init__.py
new file mode 100644
index 000000000..efc09e760
--- /dev/null
+++ b/docs/quick_tutorial/authentication/tutorial/__init__.py
@@ -0,0 +1,25 @@
+from pyramid.authentication import AuthTktAuthenticationPolicy
+from pyramid.authorization import ACLAuthorizationPolicy
+from pyramid.config import Configurator
+
+from .security import groupfinder
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+
+ # Security policies
+ authn_policy = AuthTktAuthenticationPolicy(
+ settings['tutorial.secret'], callback=groupfinder,
+ hashalg='sha512')
+ authz_policy = ACLAuthorizationPolicy()
+ config.set_authentication_policy(authn_policy)
+ config.set_authorization_policy(authz_policy)
+
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.add_route('login', '/login')
+ config.add_route('logout', '/logout')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/authentication/tutorial/home.pt b/docs/quick_tutorial/authentication/tutorial/home.pt
new file mode 100644
index 000000000..6ecd0081b
--- /dev/null
+++ b/docs/quick_tutorial/authentication/tutorial/home.pt
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+
+<div>
+ <a tal:condition="view.logged_in is None"
+ href="${request.application_url}/login">Log In</a>
+ <a tal:condition="view.logged_in is not None"
+ href="${request.application_url}/logout">Logout</a>
+</div>
+
+<h1>Hi ${name}</h1>
+<p>Visit <a href="${request.route_url('hello')}">hello</a></p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/authentication/tutorial/login.pt b/docs/quick_tutorial/authentication/tutorial/login.pt
new file mode 100644
index 000000000..4451fc4f8
--- /dev/null
+++ b/docs/quick_tutorial/authentication/tutorial/login.pt
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>Login</h1>
+<span tal:replace="message"/>
+
+<form action="${url}" method="post">
+ <input type="hidden" name="came_from"
+ value="${came_from}"/>
+ <label for="login">Username</label>
+ <input type="text" id="login"
+ name="login"
+ value="${login}"/><br/>
+ <label for="password">Password</label>
+ <input type="password" id="password"
+ name="password"
+ value="${password}"/><br/>
+ <input type="submit" name="form.submitted"
+ value="Log In"/>
+</form>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/authentication/tutorial/security.py b/docs/quick_tutorial/authentication/tutorial/security.py
new file mode 100644
index 000000000..ab90bab2c
--- /dev/null
+++ b/docs/quick_tutorial/authentication/tutorial/security.py
@@ -0,0 +1,8 @@
+USERS = {'editor': 'editor',
+ 'viewer': 'viewer'}
+GROUPS = {'editor': ['group:editors']}
+
+
+def groupfinder(userid, request):
+ if userid in USERS:
+ return GROUPS.get(userid, []) \ No newline at end of file
diff --git a/docs/quick_tutorial/authentication/tutorial/views.py b/docs/quick_tutorial/authentication/tutorial/views.py
new file mode 100644
index 000000000..3038b6d9b
--- /dev/null
+++ b/docs/quick_tutorial/authentication/tutorial/views.py
@@ -0,0 +1,64 @@
+from pyramid.httpexceptions import HTTPFound
+from pyramid.security import (
+ remember,
+ forget,
+ authenticated_userid
+ )
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+from .security import USERS
+
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+ self.logged_in = authenticated_userid(request)
+
+ @view_config(route_name='home')
+ def home(self):
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello')
+ def hello(self):
+ return {'name': 'Hello View'}
+
+ @view_config(route_name='login', renderer='login.pt')
+ def login(self):
+ request = self.request
+ login_url = request.route_url('login')
+ referrer = request.url
+ if referrer == login_url:
+ referrer = '/' # never use 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(
+ name='Login',
+ message=message,
+ url=request.application_url + '/login',
+ came_from=came_from,
+ login=login,
+ password=password,
+ )
+
+ @view_config(route_name='logout')
+ def logout(self):
+ request = self.request
+ headers = forget(request)
+ url = request.route_url('home')
+ return HTTPFound(location=url,
+ headers=headers)
diff --git a/docs/quick_tutorial/authorization.rst b/docs/quick_tutorial/authorization.rst
new file mode 100644
index 000000000..6b10d3409
--- /dev/null
+++ b/docs/quick_tutorial/authorization.rst
@@ -0,0 +1,112 @@
+===========================================
+21: Protecting Resources With Authorization
+===========================================
+
+Assign security statements to resources describing the permissions
+required to perform an operation.
+
+Background
+==========
+
+Our application has URLs that allow people to add/edit/delete content
+via a web browser. Time to add security to the application. Let's
+protect our add/edit views to require a login (username of
+``editor`` and password of ``editor``.) We will allow the other views
+to continue working without a password.
+
+Objectives
+==========
+
+- Introduce the Pyramid concepts of authentication, authorization,
+ permissions, and access control lists (ACLs)
+
+- Make a :term:`root factory` that returns an instance of our
+ class for the top of the application
+
+- Assign security statements to our root resource
+
+- Add a permissions predicate on a view
+
+- Provide a :term:`Forbidden view` to handle visiting a URL without
+ adequate permissions
+
+Steps
+=====
+
+#. We are going to use the authentication step as our starting point:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r authentication authorization; cd authorization
+ $ $VENV/bin/python setup.py develop
+
+#. Start by changing ``authorization/tutorial/__init__.py`` to
+ specify a root factory to the :term:`configurator`:
+
+ .. literalinclude:: authorization/tutorial/__init__.py
+ :linenos:
+
+#. That means we need to implement
+ ``authorization/tutorial/resources.py``
+
+ .. literalinclude:: authorization/tutorial/resources.py
+ :linenos:
+
+#. Change ``authorization/tutorial/views.py`` to require the ``edit``
+ permission on the ``hello`` view and implement the forbidden view:
+
+ .. literalinclude:: authorization/tutorial/views.py
+ :linenos:
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in a browser.
+
+#. If you are still logged in, click the "Log Out" link.
+
+#. Visit http://localhost:6543/howdy in a browser. You should be
+ asked to login.
+
+Analysis
+========
+
+This simple tutorial step can be boiled down to the following:
+
+- A view can require a *permission* (``edit``)
+
+- The context for our view (the ``Root``) has an access control list
+ (ACL)
+
+- This ACL says that the ``edit`` permission is available on ``Root``
+ to the ``group:editors`` *principal*
+
+- The registered ``groupfinder`` answers whether a particular user
+ (``editor``) has a particular group (``group:editors``)
+
+In summary: ``hello`` wants ``edit`` permission, ``Root`` says
+``group:editors`` has ``edit`` permission.
+
+Of course, this only applies on ``Root``. Some other part of the site
+(a.k.a. *context*) might have a different ACL.
+
+If you are not logged in and visit ``/hello``, you need to get
+shown the login screen. How does Pyramid know what is the login page to
+use? We explicitly told Pyramid that the ``login`` view should be used
+by decorating the view with ``@forbidden_view_config``.
+
+Extra Credit
+============
+
+#. Perhaps you would like experience of not having enough permissions
+ (forbidden) to be richer. How could you change this?
+
+#. Perhaps we want to store security statements in a database and
+ allow editing via a browser. How might this be done?
+
+#. What if we want different security statements on different kinds of
+ objects? Or on the same kinds of objects, but in different parts of a
+ URL hierarchy?
diff --git a/docs/quick_tutorial/authorization/development.ini b/docs/quick_tutorial/authorization/development.ini
new file mode 100644
index 000000000..5d4580ff5
--- /dev/null
+++ b/docs/quick_tutorial/authorization/development.ini
@@ -0,0 +1,42 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+tutorial.secret = 98zd
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/authorization/setup.py b/docs/quick_tutorial/authorization/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/authorization/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/authorization/tutorial/__init__.py b/docs/quick_tutorial/authorization/tutorial/__init__.py
new file mode 100644
index 000000000..8f7ab8277
--- /dev/null
+++ b/docs/quick_tutorial/authorization/tutorial/__init__.py
@@ -0,0 +1,26 @@
+from pyramid.authentication import AuthTktAuthenticationPolicy
+from pyramid.authorization import ACLAuthorizationPolicy
+from pyramid.config import Configurator
+
+from .security import groupfinder
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings,
+ root_factory='.resources.Root')
+ config.include('pyramid_chameleon')
+
+ # Security policies
+ authn_policy = AuthTktAuthenticationPolicy(
+ settings['tutorial.secret'], callback=groupfinder,
+ hashalg='sha512')
+ authz_policy = ACLAuthorizationPolicy()
+ config.set_authentication_policy(authn_policy)
+ config.set_authorization_policy(authz_policy)
+
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.add_route('login', '/login')
+ config.add_route('logout', '/logout')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/authorization/tutorial/home.pt b/docs/quick_tutorial/authorization/tutorial/home.pt
new file mode 100644
index 000000000..6ecd0081b
--- /dev/null
+++ b/docs/quick_tutorial/authorization/tutorial/home.pt
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+
+<div>
+ <a tal:condition="view.logged_in is None"
+ href="${request.application_url}/login">Log In</a>
+ <a tal:condition="view.logged_in is not None"
+ href="${request.application_url}/logout">Logout</a>
+</div>
+
+<h1>Hi ${name}</h1>
+<p>Visit <a href="${request.route_url('hello')}">hello</a></p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/authorization/tutorial/login.pt b/docs/quick_tutorial/authorization/tutorial/login.pt
new file mode 100644
index 000000000..4451fc4f8
--- /dev/null
+++ b/docs/quick_tutorial/authorization/tutorial/login.pt
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>Login</h1>
+<span tal:replace="message"/>
+
+<form action="${url}" method="post">
+ <input type="hidden" name="came_from"
+ value="${came_from}"/>
+ <label for="login">Username</label>
+ <input type="text" id="login"
+ name="login"
+ value="${login}"/><br/>
+ <label for="password">Password</label>
+ <input type="password" id="password"
+ name="password"
+ value="${password}"/><br/>
+ <input type="submit" name="form.submitted"
+ value="Log In"/>
+</form>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/authorization/tutorial/resources.py b/docs/quick_tutorial/authorization/tutorial/resources.py
new file mode 100644
index 000000000..0cb656f12
--- /dev/null
+++ b/docs/quick_tutorial/authorization/tutorial/resources.py
@@ -0,0 +1,9 @@
+from pyramid.security import Allow, Everyone
+
+
+class Root(object):
+ __acl__ = [(Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit')]
+
+ def __init__(self, request):
+ pass \ No newline at end of file
diff --git a/docs/quick_tutorial/authorization/tutorial/security.py b/docs/quick_tutorial/authorization/tutorial/security.py
new file mode 100644
index 000000000..ab90bab2c
--- /dev/null
+++ b/docs/quick_tutorial/authorization/tutorial/security.py
@@ -0,0 +1,8 @@
+USERS = {'editor': 'editor',
+ 'viewer': 'viewer'}
+GROUPS = {'editor': ['group:editors']}
+
+
+def groupfinder(userid, request):
+ if userid in USERS:
+ return GROUPS.get(userid, []) \ No newline at end of file
diff --git a/docs/quick_tutorial/authorization/tutorial/views.py b/docs/quick_tutorial/authorization/tutorial/views.py
new file mode 100644
index 000000000..92c1946ba
--- /dev/null
+++ b/docs/quick_tutorial/authorization/tutorial/views.py
@@ -0,0 +1,66 @@
+from pyramid.httpexceptions import HTTPFound
+from pyramid.security import (
+ remember,
+ forget,
+ authenticated_userid
+ )
+from pyramid.view import (
+ view_config,
+ view_defaults,
+ forbidden_view_config
+ )
+
+from .security import USERS
+
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+ self.logged_in = authenticated_userid(request)
+
+ @view_config(route_name='home')
+ def home(self):
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello', permission='edit')
+ def hello(self):
+ return {'name': 'Hello View'}
+
+ @view_config(route_name='login', renderer='login.pt')
+ @forbidden_view_config(renderer='login.pt')
+ def login(self):
+ request = self.request
+ login_url = request.route_url('login')
+ referrer = request.url
+ if referrer == login_url:
+ referrer = '/' # never use 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(
+ name='Login',
+ message=message,
+ url=request.application_url + '/login',
+ came_from=came_from,
+ login=login,
+ password=password,
+ )
+
+ @view_config(route_name='logout')
+ def logout(self):
+ request = self.request
+ headers = forget(request)
+ url = request.route_url('home')
+ return HTTPFound(location=url,
+ headers=headers)
diff --git a/docs/quick_tutorial/conf.py b/docs/quick_tutorial/conf.py
new file mode 100644
index 000000000..47b8fae41
--- /dev/null
+++ b/docs/quick_tutorial/conf.py
@@ -0,0 +1,281 @@
+# -*- coding: utf-8 -*-
+#
+# Getting Started with Pyramid and REST documentation build configuration file, created by
+# sphinx-quickstart on Mon Aug 26 14:44:57 2013.
+#
+# This file is execfile()d with the current directory set to its containing dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.intersphinx']
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'index'
+
+# General information about the project.
+project = u'Getting Started with Pyramid and REST'
+copyright = u'2013, Agendaless Consulting'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+version = '1.0'
+# The full version, including alpha/beta/rc tags.
+release = '1.0'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+# If true, keep warnings as "system message" paragraphs in the built documents.
+#keep_warnings = False
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages. See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further. For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents. If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar. Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it. The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'GettingStartedwithPyramidandRESTdoc'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+latex_elements = {
+ # The paper size ('letterpaper' or 'a4paper').
+ #'papersize': 'letterpaper',
+
+ # The font size ('10pt', '11pt' or '12pt').
+ #'pointsize': '10pt',
+
+ # Additional stuff for the LaTeX preamble.
+ #'preamble': '',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+ ('index', 'GettingStartedwithPyramidandREST.tex',
+ u'Getting Started with Pyramid and REST Documentation',
+ u'Agendaless Consulting', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+ ('index', 'gettingstartedwithpyramidandrest',
+ u'Getting Started with Pyramid and REST Documentation',
+ [u'Agendaless Consulting'], 1)
+]
+
+# If true, show URL addresses after external links.
+#man_show_urls = False
+
+
+# -- Options for Texinfo output ------------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+# dir menu entry, description, category)
+texinfo_documents = [
+ ('index', 'GettingStartedwithPyramidandREST',
+ u'Getting Started with Pyramid and REST Documentation',
+ u'Agendaless Consulting', 'GettingStartedwithPyramidandREST',
+ 'One line description of project.',
+ 'Miscellaneous'),
+]
+
+# Documents to append as an appendix to all manuals.
+#texinfo_appendices = []
+
+# If false, no module index is generated.
+#texinfo_domain_indices = True
+
+# How to display URL addresses: 'footnote', 'no', or 'inline'.
+#texinfo_show_urls = 'footnote'
+
+# If true, do not generate a @detailmenu in the "Top" node's menu.
+#texinfo_no_detailmenu = False
+
+
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {
+ 'python': (
+ 'http://docs.python.org/2',
+ None),
+ 'sqla': (
+ 'http://docs.sqlalchemy.org/en/latest',
+ None),
+ 'pyramid': (
+ 'http://docs.pylonsproject.org/projects/pyramid/en/latest/',
+ None),
+ 'jinja2': (
+ 'http://docs.pylonsproject.org/projects/pyramid_jinja2/en/latest/',
+ None),
+ 'toolbar': (
+ 'http://docs.pylonsproject.org/projects/pyramid_debugtoolbar/en/latest',
+ None),
+ 'deform': (
+ 'http://docs.pylonsproject.org/projects/deform/en/latest',
+ None),
+ 'colander': (
+ 'http://docs.pylonsproject.org/projects/colander/en/latest',
+ None),
+ 'tutorials': (
+ 'http://docs.pylonsproject.org/projects/pyramid_tutorials/en/latest/',
+ None),
+}
diff --git a/docs/quick_tutorial/databases.rst b/docs/quick_tutorial/databases.rst
new file mode 100644
index 000000000..93a02ffc7
--- /dev/null
+++ b/docs/quick_tutorial/databases.rst
@@ -0,0 +1,195 @@
+.. _qtut_databases:
+
+==============================
+19: Databases Using SQLAlchemy
+==============================
+
+Store/retrieve data using the SQLAlchemy ORM atop the SQLite database.
+
+Background
+==========
+
+Our Pyramid-based wiki application now needs database-backed storage of
+pages. This frequently means a SQL database. The Pyramid community
+strongly supports the
+:ref:`SQLAlchemy <sqla:index_toplevel>` project and its
+:ref:`object-relational mapper (ORM) <sqla:ormtutorial_toplevel>`
+as a convenient, Pythonic way to interface to databases.
+
+In this step we hook up SQLAlchemy to a SQLite database table,
+providing storage and retrieval for the wikipages in the previous step.
+
+.. note::
+
+ The ``alchemy`` scaffold is really helpful for getting a
+ SQLAlchemy project going, including generation of the console
+ script. Since we want to see all the decisions, we will forgo
+ convenience in this tutorial and wire it up ourselves.
+
+Objectives
+==========
+
+- Store pages in SQLite by using SQLAlchemy models
+
+- Use SQLAlchemy queries to list/add/view/edit pages
+
+- Provide a database-initialize command by writing a Pyramid *console
+ script* which can be run from the command line
+
+Steps
+=====
+
+.. warning::
+
+ Your Python might not have SQLite bundled. If not, install it into
+ your virtual environment with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/easy_install sphinx pysqlite
+
+#. We are going to use the forms step as our starting point:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r forms databases; cd databases
+
+#. We need to add some dependencies in ``databases/setup.py`` as well
+ as an "entry point" for the command-line script:
+
+ .. literalinclude:: databases/setup.py
+ :linenos:
+
+ .. note::
+
+ We aren't yet doing ``python3.3 setup.py develop`` as we
+ are changing it later.
+
+#. Our configuration file at ``databases/development.ini`` wires
+ together some new pieces:
+
+ .. literalinclude:: databases/development.ini
+ :language: ini
+
+#. This engine configuration now needs to be read into the application
+ through changes in ``databases/tutorial/__init__.py``:
+
+ .. literalinclude:: databases/tutorial/__init__.py
+ :linenos:
+
+#. Make a command-line script at ``databases/tutorial/initialize_db.py``
+ to initialize the database:
+
+ .. literalinclude:: databases/tutorial/initialize_db.py
+
+#. Since ``setup.py`` changed, we now run it:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/python setup.py develop
+
+#. The script references some models in ``databases/tutorial/models.py``:
+
+ .. literalinclude:: databases/tutorial/models.py
+ :linenos:
+
+#. Let's run this console script, thus producing our database and table:
+
+ .. code-block:: bash
+
+ $ initialize_tutorial_db development.ini
+ 2013-09-06 15:54:08,050 INFO [sqlalchemy.engine.base.Engine][MainThread] PRAGMA table_info("wikipages")
+ 2013-09-06 15:54:08,050 INFO [sqlalchemy.engine.base.Engine][MainThread] ()
+ 2013-09-06 15:54:08,051 INFO [sqlalchemy.engine.base.Engine][MainThread]
+ CREATE TABLE wikipages (
+ uid INTEGER NOT NULL,
+ title TEXT,
+ body TEXT,
+ PRIMARY KEY (uid),
+ UNIQUE (title)
+ )
+
+#. With our data now driven by SQLAlchemy queries, we need to update
+ our ``databases/tutorial/views.py``:
+
+ .. literalinclude:: databases/tutorial/views.py
+
+#. Our tests in ``databases/tutorial/tests.py`` changed to include
+ SQLAlchemy bootstrapping:
+
+ .. literalinclude:: databases/tutorial/tests.py
+ :linenos:
+
+#. Run the tests in your package using ``nose``:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests .
+ ..
+ -----------------------------------------------------------------
+ Ran 2 tests in 1.141s
+
+ OK
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in a browser.
+
+Analysis
+========
+
+Let's start with the dependencies. We made the decision to use
+``SQLAlchemy`` to talk to our database. We also, though, installed
+``pyramid_tm`` and ``zope.sqlalchemy``. Why?
+
+Pyramid has a strong orientation towards support for ``transactions``.
+Specifically, you can install a transaction manager into your app
+application, either as middleware or a Pyramid "tween". Then,
+just before you return the response, all transaction-aware parts of
+your application are executed.
+
+This means Pyramid view code usually doesn't manage transactions. If
+your view code or a template generates an error, the transaction manager
+aborts the transaction. This is a very liberating way to write code.
+
+The ``pyramid_tm`` package provides a "tween" that is configured in the
+``development.ini`` configuration file. That installs it. We then need
+a package that makes SQLAlchemy and thus the RDBMS transaction manager
+integrate with the Pyramid transaction manager. That's what
+``zope.sqlalchemy`` does.
+
+Where do we point at the location on disk for the SQLite file? In the
+configuration file. This lets consumers of our package change the
+location in a safe (non-code) way. That is, in configuration. This
+configuration-oriented approach isn't required in Pyramid; you can
+still make such statements in your ``__init__.py`` or some companion
+module.
+
+The ``initialize_tutorial_db`` is a nice example of framework support.
+You point your setup at the location of some ``[console_scripts]`` and
+these get generated into your virtualenv's ``bin`` directory. Our
+console script follows the pattern of being fed a configuration file
+with all the bootstrapping. It then opens SQLAlchemy and creates the
+root of the wiki, which also makes the SQLite file. Note the
+``with transaction.manager`` part that puts the work in the scope of a
+transaction (as we aren't inside a web request where this is done
+automatically.)
+
+The ``models.py`` does a little bit extra work to hook up SQLAlchemy
+into the Pyramid transaction manager. It then declares the model for a
+``Page``.
+
+Our views have changes primarily around replacing our dummy
+dictionary-of-dictionaries data with proper database support: list the
+rows, add a row, edit a row, and delete a row.
+
+Extra Credit
+============
+
+#. Why all this code? Why can't I just type 2 lines and have magic ensue?
+
+#. Give a try at a button that deletes a wiki page.
diff --git a/docs/quick_tutorial/databases/development.ini b/docs/quick_tutorial/databases/development.ini
new file mode 100644
index 000000000..270da960f
--- /dev/null
+++ b/docs/quick_tutorial/databases/development.ini
@@ -0,0 +1,49 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+ pyramid_tm
+
+sqlalchemy.url = sqlite:///%(here)s/sqltutorial.sqlite
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial, sqlalchemy
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[logger_sqlalchemy]
+level = INFO
+handlers =
+qualname = sqlalchemy.engine
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/databases/setup.py b/docs/quick_tutorial/databases/setup.py
new file mode 100644
index 000000000..238358fe4
--- /dev/null
+++ b/docs/quick_tutorial/databases/setup.py
@@ -0,0 +1,20 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon',
+ 'deform',
+ 'sqlalchemy',
+ 'pyramid_tm',
+ 'zope.sqlalchemy'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ [console_scripts]
+ initialize_tutorial_db = tutorial.initialize_db:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/databases/sqltutorial.sqlite b/docs/quick_tutorial/databases/sqltutorial.sqlite
new file mode 100644
index 000000000..b8bd856fd
--- /dev/null
+++ b/docs/quick_tutorial/databases/sqltutorial.sqlite
Binary files differ
diff --git a/docs/quick_tutorial/databases/tutorial/__init__.py b/docs/quick_tutorial/databases/tutorial/__init__.py
new file mode 100644
index 000000000..74aa25740
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/__init__.py
@@ -0,0 +1,21 @@
+from pyramid.config import Configurator
+
+from sqlalchemy import engine_from_config
+
+from .models import DBSession, Base
+
+def main(global_config, **settings):
+ engine = engine_from_config(settings, 'sqlalchemy.')
+ DBSession.configure(bind=engine)
+ Base.metadata.bind = engine
+
+ config = Configurator(settings=settings,
+ root_factory='tutorial.models.Root')
+ config.include('pyramid_chameleon')
+ config.add_route('wiki_view', '/')
+ config.add_route('wikipage_add', '/add')
+ config.add_route('wikipage_view', '/{uid}')
+ config.add_route('wikipage_edit', '/{uid}/edit')
+ config.add_static_view('deform_static', 'deform:static/')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/databases/tutorial/initialize_db.py b/docs/quick_tutorial/databases/tutorial/initialize_db.py
new file mode 100644
index 000000000..98be524a1
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/initialize_db.py
@@ -0,0 +1,37 @@
+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):
+ 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(title='Root', body='<p>Root</p>')
+ DBSession.add(model)
diff --git a/docs/quick_tutorial/databases/tutorial/models.py b/docs/quick_tutorial/databases/tutorial/models.py
new file mode 100644
index 000000000..b27c38417
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/models.py
@@ -0,0 +1,35 @@
+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):
+ __tablename__ = 'wikipages'
+ uid = Column(Integer, primary_key=True)
+ title = Column(Text, unique=True)
+ body = Column(Text)
+
+
+class Root(object):
+ __acl__ = [(Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit')]
+
+ def __init__(self, request):
+ pass \ No newline at end of file
diff --git a/docs/quick_tutorial/databases/tutorial/tests.py b/docs/quick_tutorial/databases/tutorial/tests.py
new file mode 100644
index 000000000..e18e70c8c
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/tests.py
@@ -0,0 +1,58 @@
+import unittest
+import transaction
+
+from pyramid import testing
+
+
+def _initTestingDB():
+ from sqlalchemy import create_engine
+ from .models import (
+ DBSession,
+ Page,
+ Base
+ )
+ engine = create_engine('sqlite://')
+ Base.metadata.create_all(engine)
+ DBSession.configure(bind=engine)
+ with transaction.manager:
+ model = Page(title='FrontPage', body='This is the front page')
+ DBSession.add(model)
+ return DBSession
+
+
+class WikiViewTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ self.session.remove()
+ testing.tearDown()
+
+ def test_wiki_view(self):
+ from tutorial.views import WikiViews
+
+ request = testing.DummyRequest()
+ inst = WikiViews(request)
+ response = inst.wiki_view()
+ self.assertEqual(response['title'], 'Wiki View')
+
+
+class WikiFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ self.session = _initTestingDB()
+ self.config = testing.setUp()
+ from pyramid.paster import get_app
+ app = get_app('development.ini')
+ from webtest import TestApp
+ self.testapp = TestApp(app)
+
+ def tearDown(self):
+ self.session.remove()
+ testing.tearDown()
+
+ def test_it(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'Wiki: View', res.body)
+ res = self.testapp.get('/add', status=200)
+ self.assertIn(b'Add/Edit', res.body)
diff --git a/docs/quick_tutorial/databases/tutorial/views.py b/docs/quick_tutorial/databases/tutorial/views.py
new file mode 100644
index 000000000..4608c6d43
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/views.py
@@ -0,0 +1,96 @@
+import colander
+import deform.widget
+
+from pyramid.httpexceptions import HTTPFound
+from pyramid.view import view_config
+
+from .models import DBSession, Page
+
+
+class WikiPage(colander.MappingSchema):
+ title = colander.SchemaNode(colander.String())
+ body = colander.SchemaNode(
+ colander.String(),
+ widget=deform.widget.RichTextWidget()
+ )
+
+
+class WikiViews(object):
+ def __init__(self, request):
+ self.request = request
+
+ @property
+ def wiki_form(self):
+ schema = WikiPage()
+ return deform.Form(schema, buttons=('submit',))
+
+ @property
+ def reqts(self):
+ return self.wiki_form.get_widget_resources()
+
+ @view_config(route_name='wiki_view', renderer='wiki_view.pt')
+ def wiki_view(self):
+ pages = DBSession.query(Page).order_by(Page.title)
+ return dict(title='Wiki View', pages=pages)
+
+ @view_config(route_name='wikipage_add',
+ renderer='wikipage_addedit.pt')
+ def wikipage_add(self):
+ form = self.wiki_form.render()
+
+ if 'submit' in self.request.params:
+ controls = self.request.POST.items()
+ try:
+ appstruct = self.wiki_form.validate(controls)
+ except deform.ValidationFailure as e:
+ # Form is NOT valid
+ return dict(form=e.render())
+
+ # Add a new page to the database
+ new_title = appstruct['title']
+ new_body = appstruct['body']
+ DBSession.add(Page(title=new_title, body=new_body))
+
+ # Get the new ID and redirect
+ page = DBSession.query(Page).filter_by(title=new_title).one()
+ new_uid = page.uid
+
+ url = self.request.route_url('wikipage_view', uid=new_uid)
+ return HTTPFound(url)
+
+ return dict(form=form)
+
+
+ @view_config(route_name='wikipage_view', renderer='wikipage_view.pt')
+ def wikipage_view(self):
+ uid = int(self.request.matchdict['uid'])
+ page = DBSession.query(Page).filter_by(uid=uid).one()
+ return dict(page=page)
+
+
+ @view_config(route_name='wikipage_edit',
+ renderer='wikipage_addedit.pt')
+ def wikipage_edit(self):
+ uid = int(self.request.matchdict['uid'])
+ page = DBSession.query(Page).filter_by(uid=uid).one()
+
+ wiki_form = self.wiki_form
+
+ if 'submit' in self.request.params:
+ controls = self.request.POST.items()
+ try:
+ appstruct = wiki_form.validate(controls)
+ except deform.ValidationFailure as e:
+ return dict(page=page, form=e.render())
+
+ # Change the content and redirect to the view
+ page.title = appstruct['title']
+ page.body = appstruct['body']
+ url = self.request.route_url('wikipage_view', uid=uid)
+ return HTTPFound(url)
+
+ form = self.wiki_form.render(dict(
+ uid=page.uid, title=page.title, body=page.body)
+ )
+
+ return dict(page=page, form=form)
diff --git a/docs/quick_tutorial/databases/tutorial/wiki_view.pt b/docs/quick_tutorial/databases/tutorial/wiki_view.pt
new file mode 100644
index 000000000..9e3afe495
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/wiki_view.pt
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Wiki: View</title>
+</head>
+<body>
+<h1>Wiki</h1>
+
+<a href="${request.route_url('wikipage_add')}">Add
+ WikiPage</a>
+<ul>
+ <li tal:repeat="page pages">
+ <a href="${request.route_url('wikipage_view', uid=page.uid)}">
+ ${page.title}
+ </a>
+ </li>
+</ul>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
new file mode 100644
index 000000000..d1fea0d7f
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/wikipage_addedit.pt
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>WikiPage: Add/Edit</title>
+ <tal:block tal:repeat="reqt view.reqts['css']">
+ <link rel="stylesheet" type="text/css"
+ href="${request.static_url('deform:static/' + reqt)}"/>
+ </tal:block>
+ <tal:block tal:repeat="reqt view.reqts['js']">
+ <script src="${request.static_url('deform:static/' + reqt)}"
+ type="text/javascript"></script>
+ </tal:block>
+</head>
+<body>
+<h1>Wiki</h1>
+
+<p>${structure: form}</p>
+<script type="text/javascript">
+ deform.load()
+</script>
+</body>
+</html>
diff --git a/docs/quick_tutorial/databases/tutorial/wikipage_view.pt b/docs/quick_tutorial/databases/tutorial/wikipage_view.pt
new file mode 100644
index 000000000..cb9ff526e
--- /dev/null
+++ b/docs/quick_tutorial/databases/tutorial/wikipage_view.pt
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>WikiPage: View</title>
+</head>
+<body>
+<a href="${request.route_url('wiki_view')}">
+ Up
+</a> |
+<a href="${request.route_url('wikipage_edit', uid=page.uid)}">
+ Edit
+</a>
+
+<h1>${page.title}</h1>
+<p>${structure: page.body}</p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/debugtoolbar.rst b/docs/quick_tutorial/debugtoolbar.rst
new file mode 100644
index 000000000..d25588c49
--- /dev/null
+++ b/docs/quick_tutorial/debugtoolbar.rst
@@ -0,0 +1,89 @@
+.. _qtut_debugtoolbar:
+
+============================================
+04: Easier Development with ``debugtoolbar``
+============================================
+
+Error-handling and introspection using the ``pyramid_debugtoolbar``
+add-on.
+
+Background
+==========
+
+As we introduce the basics we also want to show how to be productive in
+development and debugging. For example, we just discussed template
+reloading and earlier we showed ``--reload`` for application reloading.
+
+``pyramid_debugtoolbar`` is a popular Pyramid add-on which makes
+several tools available in your browser. Adding it to your project
+illustrates several points about configuration.
+
+Objectives
+==========
+
+- Install and enable the toolbar to help during development
+
+- Explain Pyramid add-ons
+
+- Show how an add-on gets configured into your application
+
+Steps
+=====
+
+#. First we copy the results of the previous step, as well as install
+ the ``pyramid_debugtoolbar`` package:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r ini debugtoolbar; cd debugtoolbar
+ $ $VENV/bin/python setup.py develop
+ $ $VENV/bin/easy_install pyramid_debugtoolbar
+
+
+#. Our ``debugtoolbar/development.ini`` gets a configuration entry for
+ ``pyramid.includes``:
+
+ .. literalinclude:: debugtoolbar/development.ini
+ :language: ini
+ :linenos:
+
+#. Run the WSGI application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in your browser. See the handy
+ toolbar on the right.
+
+Analysis
+========
+
+``pyramid_debugtoolbar`` is a full-fledged Python package,
+available on PyPI just like thousands of other Python packages. Thus we
+start by installing the ``pyramid_debugtoolbar`` package into our
+virtual environment using normal Python package installation commands.
+
+The ``pyramid_debugtoolbar`` Python package is also a Pyramid add-on,
+which means we need to include its add-on configuration into our web
+application. We could do this with imperative configuration in
+``tutorial/__init__.py`` by using ``config.include``. Pyramid also
+supports wiring in add-on configuration via our ``development.ini``
+using ``pyramid.includes``. We use this to load the configuration for
+the debugtoolbar.
+
+You'll now see an attractive (and collapsible) menu in the right of
+your browser, providing introspective access to debugging information.
+Even better, if your web application generates an error,
+you will see a nice traceback on the screen. When you want to disable
+this toolbar, no need to change code: you can remove it from
+``pyramid.includes`` in the relevant ``.ini`` configuration file (thus
+showing why configuration files are handy.)
+
+Note that the toolbar mutates the HTML generated by our app and uses jQuery to
+overlay itself. If you are using the toolbar while you're developing and you
+start to experience otherwise inexplicable client-side weirdness, you can shut
+it off by commenting out the ``pyramid_debugtoolbar`` line in
+``pyramid.includes`` temporarily.
+
+.. seealso:: See Also: :ref:`pyramid_debugtoolbar <toolbar:overview>`
diff --git a/docs/quick_tutorial/debugtoolbar/development.ini b/docs/quick_tutorial/debugtoolbar/development.ini
new file mode 100644
index 000000000..470d92c57
--- /dev/null
+++ b/docs/quick_tutorial/debugtoolbar/development.ini
@@ -0,0 +1,40 @@
+[app:main]
+use = egg:tutorial
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/debugtoolbar/setup.py b/docs/quick_tutorial/debugtoolbar/setup.py
new file mode 100644
index 000000000..9997984d3
--- /dev/null
+++ b/docs/quick_tutorial/debugtoolbar/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/debugtoolbar/tutorial/__init__.py b/docs/quick_tutorial/debugtoolbar/tutorial/__init__.py
new file mode 100644
index 000000000..2b4e84f30
--- /dev/null
+++ b/docs/quick_tutorial/debugtoolbar/tutorial/__init__.py
@@ -0,0 +1,13 @@
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ return Response('<body><h1>Hello World!</h1></body>')
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/forms.rst b/docs/quick_tutorial/forms.rst
new file mode 100644
index 000000000..e8bc0c8b4
--- /dev/null
+++ b/docs/quick_tutorial/forms.rst
@@ -0,0 +1,148 @@
+.. _qtut_forms:
+
+====================================
+18: Forms and Validation With Deform
+====================================
+
+Schema-driven, autogenerated forms with validation.
+
+Background
+==========
+
+Modern web applications deal extensively with forms. Developers,
+though, have a wide range of philosophies about how frameworks should
+help them with their forms. As such, Pyramid doesn't directly bundle
+one particular form library. Instead, there are a variety of form
+libraries that are easy to use in Pyramid.
+
+:ref:`Deform <deform:overview>`
+is one such library. In this step, we introduce Deform for our
+forms and validation. This also gives us the
+:ref:`Colander <colander:overview>` for schemas and validation.
+
+Deform is getting a facelift, with styling from Twitter Bootstrap and
+advanced widgets from popular JavaScript projects. The work began in
+``deform_bootstrap`` and is being merged into an update to Deform.
+
+Objectives
+==========
+
+- Make a schema using Colander, the companion to Deform
+
+- Create a form with Deform and change our views to handle validation
+
+Steps
+=====
+
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes forms; cd forms
+
+#. Let's edit ``forms/setup.py`` to declare a dependency on Deform
+ (which then pulls in Colander as a dependency:
+
+ .. literalinclude:: forms/setup.py
+ :linenos:
+
+#. We can now install our project in development mode:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/python setup.py develop
+
+#. Register a static view in ``forms/tutorial/__init__.py`` for
+ Deform's CSS/JS etc. as well as our demo wikipage scenario's
+ views:
+
+ .. literalinclude:: forms/tutorial/__init__.py
+ :linenos:
+
+#. Implement the new views, as well as the form schemas and some
+ dummy data, in ``forms/tutorial/views.py``:
+
+ .. literalinclude:: forms/tutorial/views.py
+ :linenos:
+
+#. A template for the top of the "wiki" in
+ ``forms/tutorial/wiki_view.pt``:
+
+ .. literalinclude:: forms/tutorial/wiki_view.pt
+ :language: html
+ :linenos:
+
+#. Another template for adding/editing in
+ ``forms/tutorial/wikipage_addedit.pt``:
+
+ .. literalinclude:: forms/tutorial/wikipage_addedit.pt
+ :language: html
+ :linenos:
+
+#. Finally, a template at ``forms/tutorial/wikipage_view.pt``
+ for viewing a wiki page:
+
+ .. literalinclude:: forms/tutorial/wikipage_view.pt
+ :language: html
+ :linenos:
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in a browser.
+
+
+Analysis
+========
+
+This step helps illustrate the utility of asset specifications for
+static assets. We have an outside package called Deform with static
+assets which need to be published. We don't have to know where on disk
+it is located. We point at the package, then the path inside the package.
+
+We just need to include a call to ``add_static_view`` to make that
+directory available at a URL. For Pyramid-specific pages,
+Pyramid provides a facility (``config.include()``) which even makes
+that unnecessary for consumers of a package. (Deform is not specific to
+Pyramid.)
+
+Our forms have rich widgets which need the static CSS and JS just
+mentioned. Deform has a :term:`resource registry` which allows widgets
+to specify which JS and CSS are needed. Our ``wikipage_addedit.pt``
+template shows how we iterated over that data to generate markup that
+includes the needed resources.
+
+Our add and edit views use a pattern called *self-posting forms*.
+Meaning, the same URL is used to ``GET`` the form as is used to
+``POST`` the form. The route, the view, and the template are the same
+whether you are walking up to it the first time or you clicked a button.
+
+Inside the view we do ``if 'submit' in self.request.params:`` to see if
+this form was a ``POST`` where the user clicked on a particular button
+``<input name="submit">``.
+
+The form controller then follows a typical pattern:
+
+- If you are doing a GET, skip over and just return the form
+
+- If you are doing a POST, validate the form contents
+
+- If the form is invalid, bail out by re-rendering the form with the
+ supplied ``POST`` data
+
+- If the validation succeeded, perform some action and issue a
+ redirect via ``HTTPFound``.
+
+We are, in essence, writing our own form controller. Other
+Pyramid-based systems, including ``pyramid_deform``, provide a
+form-centric view class which automates much of this branching and
+routing.
+
+Extra Credit
+============
+
+#. Give a try at a button that goes to a delete view for a
+ particular wiki page.
diff --git a/docs/quick_tutorial/forms/development.ini b/docs/quick_tutorial/forms/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/forms/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/forms/setup.py b/docs/quick_tutorial/forms/setup.py
new file mode 100644
index 000000000..361ade013
--- /dev/null
+++ b/docs/quick_tutorial/forms/setup.py
@@ -0,0 +1,15 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon',
+ 'deform'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/forms/tutorial/__init__.py b/docs/quick_tutorial/forms/tutorial/__init__.py
new file mode 100644
index 000000000..dff7457cf
--- /dev/null
+++ b/docs/quick_tutorial/forms/tutorial/__init__.py
@@ -0,0 +1,13 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('wiki_view', '/')
+ config.add_route('wikipage_add', '/add')
+ config.add_route('wikipage_view', '/{uid}')
+ config.add_route('wikipage_edit', '/{uid}/edit')
+ config.add_static_view('deform_static', 'deform:static/')
+ config.scan('.views')
+ return config.make_wsgi_app()
diff --git a/docs/quick_tutorial/forms/tutorial/tests.py b/docs/quick_tutorial/forms/tutorial/tests.py
new file mode 100644
index 000000000..5a2c40904
--- /dev/null
+++ b/docs/quick_tutorial/forms/tutorial/tests.py
@@ -0,0 +1,36 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import WikiViews
+
+ request = testing.DummyRequest()
+ inst = WikiViews(request)
+ response = inst.wiki_view()
+ self.assertEqual(len(response['pages']), 3)
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<title>Wiki: View</title>', res.body)
diff --git a/docs/quick_tutorial/forms/tutorial/views.py b/docs/quick_tutorial/forms/tutorial/views.py
new file mode 100644
index 000000000..004d2aba9
--- /dev/null
+++ b/docs/quick_tutorial/forms/tutorial/views.py
@@ -0,0 +1,96 @@
+import colander
+import deform.widget
+
+from pyramid.httpexceptions import HTTPFound
+from pyramid.view import view_config
+
+pages = {
+ '100': dict(uid='100', title='Page 100', body='<em>100</em>'),
+ '101': dict(uid='101', title='Page 101', body='<em>101</em>'),
+ '102': dict(uid='102', title='Page 102', body='<em>102</em>')
+}
+
+class WikiPage(colander.MappingSchema):
+ title = colander.SchemaNode(colander.String())
+ body = colander.SchemaNode(
+ colander.String(),
+ widget=deform.widget.RichTextWidget()
+ )
+
+
+class WikiViews(object):
+ def __init__(self, request):
+ self.request = request
+
+ @property
+ def wiki_form(self):
+ schema = WikiPage()
+ return deform.Form(schema, buttons=('submit',))
+
+ @property
+ def reqts(self):
+ return self.wiki_form.get_widget_resources()
+
+ @view_config(route_name='wiki_view', renderer='wiki_view.pt')
+ def wiki_view(self):
+ return dict(pages=pages.values())
+
+ @view_config(route_name='wikipage_add',
+ renderer='wikipage_addedit.pt')
+ def wikipage_add(self):
+ form = self.wiki_form.render()
+
+ if 'submit' in self.request.params:
+ controls = self.request.POST.items()
+ try:
+ appstruct = self.wiki_form.validate(controls)
+ except deform.ValidationFailure as e:
+ # Form is NOT valid
+ return dict(form=e.render())
+
+ # Form is valid, make a new identifier and add to list
+ last_uid = int(sorted(pages.keys())[-1])
+ new_uid = str(last_uid + 1)
+ pages[new_uid] = dict(
+ uid=new_uid, title=appstruct['title'],
+ body=appstruct['body']
+ )
+
+ # Now visit new page
+ url = self.request.route_url('wikipage_view', uid=new_uid)
+ return HTTPFound(url)
+
+ return dict(form=form)
+
+ @view_config(route_name='wikipage_view', renderer='wikipage_view.pt')
+ def wikipage_view(self):
+ uid = self.request.matchdict['uid']
+ page = pages[uid]
+ return dict(page=page)
+
+ @view_config(route_name='wikipage_edit',
+ renderer='wikipage_addedit.pt')
+ def wikipage_edit(self):
+ uid = self.request.matchdict['uid']
+ page = pages[uid]
+
+ wiki_form = self.wiki_form
+
+ if 'submit' in self.request.params:
+ controls = self.request.POST.items()
+ try:
+ appstruct = wiki_form.validate(controls)
+ except deform.ValidationFailure as e:
+ return dict(page=page, form=e.render())
+
+ # Change the content and redirect to the view
+ page['title'] = appstruct['title']
+ page['body'] = appstruct['body']
+
+ url = self.request.route_url('wikipage_view',
+ uid=page['uid'])
+ return HTTPFound(url)
+
+ form = wiki_form.render(page)
+
+ return dict(page=page, form=form) \ No newline at end of file
diff --git a/docs/quick_tutorial/forms/tutorial/wiki_view.pt b/docs/quick_tutorial/forms/tutorial/wiki_view.pt
new file mode 100644
index 000000000..9e3afe495
--- /dev/null
+++ b/docs/quick_tutorial/forms/tutorial/wiki_view.pt
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Wiki: View</title>
+</head>
+<body>
+<h1>Wiki</h1>
+
+<a href="${request.route_url('wikipage_add')}">Add
+ WikiPage</a>
+<ul>
+ <li tal:repeat="page pages">
+ <a href="${request.route_url('wikipage_view', uid=page.uid)}">
+ ${page.title}
+ </a>
+ </li>
+</ul>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
new file mode 100644
index 000000000..d1fea0d7f
--- /dev/null
+++ b/docs/quick_tutorial/forms/tutorial/wikipage_addedit.pt
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>WikiPage: Add/Edit</title>
+ <tal:block tal:repeat="reqt view.reqts['css']">
+ <link rel="stylesheet" type="text/css"
+ href="${request.static_url('deform:static/' + reqt)}"/>
+ </tal:block>
+ <tal:block tal:repeat="reqt view.reqts['js']">
+ <script src="${request.static_url('deform:static/' + reqt)}"
+ type="text/javascript"></script>
+ </tal:block>
+</head>
+<body>
+<h1>Wiki</h1>
+
+<p>${structure: form}</p>
+<script type="text/javascript">
+ deform.load()
+</script>
+</body>
+</html>
diff --git a/docs/quick_tutorial/forms/tutorial/wikipage_view.pt b/docs/quick_tutorial/forms/tutorial/wikipage_view.pt
new file mode 100644
index 000000000..cb9ff526e
--- /dev/null
+++ b/docs/quick_tutorial/forms/tutorial/wikipage_view.pt
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>WikiPage: View</title>
+</head>
+<body>
+<a href="${request.route_url('wiki_view')}">
+ Up
+</a> |
+<a href="${request.route_url('wikipage_edit', uid=page.uid)}">
+ Edit
+</a>
+
+<h1>${page.title}</h1>
+<p>${structure: page.body}</p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/functional_testing.rst b/docs/quick_tutorial/functional_testing.rst
new file mode 100644
index 000000000..205ddf5cb
--- /dev/null
+++ b/docs/quick_tutorial/functional_testing.rst
@@ -0,0 +1,70 @@
+.. _qtut_functional_testing:
+
+===================================
+06: Functional Testing with WebTest
+===================================
+
+Write end-to-end full-stack testing using ``webtest``.
+
+Background
+==========
+
+Unit tests are a common and popular approach to test-driven development
+(TDD.) In web applications, though, the templating and entire apparatus
+of a web site are important parts of the delivered quality. We'd like a
+way to test these.
+
+WebTest is a Python package that does functional testing. With WebTest
+you can write tests which simulate a full HTTP request against a WSGI
+application, then test the information in the response. For speed
+purposes, WebTest skips the setup/teardown of an actual HTTP server,
+providing tests that run fast enough to be part of TDD.
+
+Objectives
+==========
+
+- Write a test which checks the contents of the returned HTML
+
+Steps
+=====
+
+#. First we copy the results of the previous step, as well as install
+ the ``webtest`` package:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r unit_testing functional_testing; cd functional_testing
+ $ $VENV/bin/python setup.py develop
+ $ $VENV/bin/easy_install webtest
+
+#. Let's extend ``unit_testing/tutorial/tests.py`` to include a
+ functional test:
+
+ .. literalinclude:: functional_testing/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+
+ $ $VENV/bin/nosetests tutorial
+ .
+ ----------------------------------------------------------------------
+ Ran 2 tests in 0.141s
+
+ OK
+
+Analysis
+========
+
+We now have the end-to-end testing we were looking for. WebTest lets us
+simply extend our existing ``nose``-based test approach with functional
+tests that are reported in the same output. These new tests not only
+cover our templating, but they didn't dramatically increase the
+execution time of our tests.
+
+Extra Credit
+============
+
+#. Why do our functional tests use ``b''``? \ No newline at end of file
diff --git a/docs/quick_tutorial/functional_testing/development.ini b/docs/quick_tutorial/functional_testing/development.ini
new file mode 100644
index 000000000..470d92c57
--- /dev/null
+++ b/docs/quick_tutorial/functional_testing/development.ini
@@ -0,0 +1,40 @@
+[app:main]
+use = egg:tutorial
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/functional_testing/setup.py b/docs/quick_tutorial/functional_testing/setup.py
new file mode 100644
index 000000000..9997984d3
--- /dev/null
+++ b/docs/quick_tutorial/functional_testing/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/functional_testing/tutorial/__init__.py b/docs/quick_tutorial/functional_testing/tutorial/__init__.py
new file mode 100644
index 000000000..2b4e84f30
--- /dev/null
+++ b/docs/quick_tutorial/functional_testing/tutorial/__init__.py
@@ -0,0 +1,13 @@
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ return Response('<body><h1>Hello World!</h1></body>')
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/functional_testing/tutorial/tests.py b/docs/quick_tutorial/functional_testing/tutorial/tests.py
new file mode 100644
index 000000000..4248acbe7
--- /dev/null
+++ b/docs/quick_tutorial/functional_testing/tutorial/tests.py
@@ -0,0 +1,31 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_hello_world(self):
+ from tutorial import hello_world
+
+ request = testing.DummyRequest()
+ response = hello_world(request)
+ self.assertEqual(response.status_code, 200)
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_hello_world(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hello World!</h1>', res.body)
diff --git a/docs/quick_tutorial/hello_world.rst b/docs/quick_tutorial/hello_world.rst
new file mode 100644
index 000000000..c7a8eaf5e
--- /dev/null
+++ b/docs/quick_tutorial/hello_world.rst
@@ -0,0 +1,114 @@
+.. _qtut_hello_world:
+
+================================
+01: Single-File Web Applications
+================================
+
+What's the simplest way to get started in Pyramid? A single-file module.
+No Python packages, no ``setup.py``, no other machinery.
+
+Background
+==========
+
+Microframeworks are all the rage these days. "Microframework" is a
+marketing term, not a technical one. They have a low mental overhead:
+they do so little, the only things you have to worry about are *your
+things*.
+
+Pyramid is special because it can act as a single-file module
+microframework. You can have a single Python file that can be executed
+directly by Python. But Pyramid also provides facilities to scale to
+the largest of applications.
+
+Python has a standard called :term:`WSGI` that defines how
+Python web applications plug into standard servers, getting passed
+incoming requests and returning responses. Most modern Python web
+frameworks obey an "MVC" (model-view-controller) application pattern,
+where the data in the model has a view that mediates interaction with
+outside systems.
+
+In this step we'll see a brief glimpse of WSGI servers, WSGI
+applications, requests, responses, and views.
+
+Objectives
+==========
+
+- Get a running Pyramid web application, as simply as possible
+
+- Use that as a well-understood base for adding each unit of complexity
+
+- Initial exposure to WSGI apps, requests, views, and responses
+
+Steps
+=====
+
+#. Make sure you have followed the steps in :doc:`requirements`.
+
+#. Starting from your workspace directory
+ (``~/projects/quick_tutorial``), create a directory for this step:
+
+ .. code-block:: bash
+
+ $ mkdir hello_world; cd hello_world
+
+#. Copy the following into ``hello_world/app.py``:
+
+ .. literalinclude:: hello_world/app.py
+ :linenos:
+
+#. Run the application:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/python app.py
+
+#. Open http://localhost:6543/ in your browser.
+
+Analysis
+========
+
+New to Python web programming? If so, some lines in module merit
+explanation:
+
+#. *Line 11*. The ``if __name__ == '__main__':`` is Python's way of
+ saying "Start here when running from the command line".
+
+#. *Lines 12-14*. Use Pyramid's :term:`configurator` to connect
+ :term:`view` code to a particular URL
+ :term:`route`.
+
+#. *Lines 6-7*. Implement the view code that generates the
+ :term:`response`.
+
+#. *Lines 15-17*. Publish a :term:`WSGI` app using an HTTP
+ server.
+
+As shown in this example, the :term:`configurator` plays a
+central role in Pyramid development. Building an application from
+loosely-coupled parts via :ref:`configuration_narr` is a
+central idea in Pyramid, one that we will revisit regularly in this
+*Quick Tour*.
+
+Extra Credit
+============
+
+#. Why do we do this:
+
+ .. code-block:: python
+
+ print ('Starting up server on http://localhost:6547')
+
+ ...instead of:
+
+ .. code-block:: python
+
+ print 'Starting up server on http://localhost:6547'
+
+#. What happens if you return a string of HTML? A sequence of integers?
+
+#. Put something invalid, such as ``print xyz``, in the view function.
+ Kill your ``python app.py`` with ``cntrl-c`` and restart,
+ then reload your browser. See the exception in the console?
+
+#. The ``GI`` in ``WSGI`` stands for "Gateway Interface". What web
+ standard is this modelled after? \ No newline at end of file
diff --git a/docs/quick_tutorial/hello_world/app.py b/docs/quick_tutorial/hello_world/app.py
new file mode 100644
index 000000000..210075023
--- /dev/null
+++ b/docs/quick_tutorial/hello_world/app.py
@@ -0,0 +1,17 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ print ('Incoming request')
+ return Response('<body><h1>Hello World!</h1></body>')
+
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tutorial/index.rst b/docs/quick_tutorial/index.rst
new file mode 100644
index 000000000..9373fe38a
--- /dev/null
+++ b/docs/quick_tutorial/index.rst
@@ -0,0 +1,50 @@
+.. _quick_tutorial:
+
+==========================
+Quick Tutorial for Pyramid
+==========================
+
+Pyramid is a web framework for Python 2 and 3. This tutorial gives a
+Python 3/2-compatible, high-level tour of the major features.
+
+This hands-on tutorial covers "a little about a lot": practical
+introductions to the most common facilities. Fun, fast-paced, and most
+certainly not aimed at experts of the Pyramid web framework.
+
+Contents
+========
+
+.. toctree::
+ :maxdepth: 1
+
+ requirements
+ tutorial_approach
+ scaffolds
+ hello_world
+ package
+ ini
+ debugtoolbar
+ unit_testing
+ functional_testing
+ views
+ templating
+ view_classes
+ request_response
+ routing
+ jinja2
+ static_assets
+ json
+ more_view_classes
+ logging
+ sessions
+ forms
+ databases
+ authentication
+ authorization
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/quick_tutorial/ini.rst b/docs/quick_tutorial/ini.rst
new file mode 100644
index 000000000..630b1faa5
--- /dev/null
+++ b/docs/quick_tutorial/ini.rst
@@ -0,0 +1,146 @@
+.. _qtut_ini:
+
+=================================================
+03: Application Configuration with ``.ini`` Files
+=================================================
+
+Use Pyramid's ``pserve`` command with a ``.ini`` configuration file for
+simpler, better application running.
+
+Background
+==========
+
+Pyramid has a first-class concept of
+:ref:`configuration <configuration_narr>` distinct from code.
+This approach is optional, but its presence makes it distinct from
+other Python web frameworks. It taps into Python's ``setuptools``
+library, which establishes conventions for how Python projects can be
+installed and provide "entry points". Pyramid uses an entry point to
+let a Pyramid application it where to find the WSGI app.
+
+Objectives
+==========
+
+- Modify our ``setup.py`` to have an entry point telling Pyramid the
+ location of the WSGI app
+
+- Create an application driven by a ``.ini`` file
+
+- Startup the application with Pyramid's ``pserve`` command
+
+- Move code into the package's ``__init__.py``
+
+Steps
+=====
+
+#. First we copy the results of the previous step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r package ini; cd ini
+
+#. Our ``ini/setup.py`` needs a setuptools "entry point" in the
+ ``setup()`` function:
+
+ .. literalinclude:: ini/setup.py
+ :linenos:
+
+#. We can now install our project, thus generating (or re-generating) an
+ "egg" at ``ini/tutorial.egg-info``:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/python setup.py develop
+
+#. Let's make a file ``ini/development.ini`` for our configuration:
+
+ .. literalinclude:: ini/development.ini
+ :language: ini
+ :linenos:
+
+#. We can refactor our startup code from the previous step's ``app.py``
+ into ``ini/tutorial/__init__.py``:
+
+ .. literalinclude:: ini/tutorial/__init__.py
+ :linenos:
+
+#. Now that ``ini/tutorial/app.py`` isn't used, let's remove it:
+
+ .. code-block:: bash
+
+ $ rm tutorial/app.py
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/.
+
+Analysis
+========
+
+Our ``development.ini`` file is read by ``pserve`` and serves to
+bootstrap our application. Processing then proceeds as described in
+the Pyramid chapter on
+:ref:`application startup <startup_chapter>`:
+
+- ``pserve`` looks for ``[app:main]`` and finds ``use = egg:tutorial``
+
+- The projects's ``setup.py`` has defined an "entry point" (lines 9-10)
+ for the project "main" entry point of ``tutorial:main``
+
+- The ``tutorial`` package's ``__init__`` has a ``main`` function
+
+- This function is invoked, with the values from certain ``.ini``
+ sections passed in
+
+The ``.ini`` file is also used for two other functions:
+
+- *Choice of WSGI server*. ``[server:main]`` wires up the choice of WSGI
+ *server* for your WSGI *application*. In this case, we are using
+ ``wsgiref`` bundled in the Python library.
+
+- *Python logging*. Pyramid uses Python standard logging, which needs a
+ number of configuration values. The ``.ini`` serves this function.
+ This provides the console log output that you see on startup and each
+ request.
+
+- *Port number*. ``port = 6543`` tells ``wsgiref`` to listen on port
+ 6543.
+
+We moved our startup code from ``app.py`` to the package's
+``tutorial/__init__.py``. This isn't necessary,
+but it is a common style in Pyramid to take the WSGI app bootstrapping
+out of your module's code and put it in the package's ``__init__.py``.
+
+The ``pserve`` application runner has a number of command-line arguments
+and options. We are using ``--reload`` which tells ``pserve`` to watch
+the filesystem for changes to relevant code (Python files, the INI file,
+etc.) and, when something changes, restart the application. Very handy
+during development.
+
+Extra Credit
+============
+
+#. If you don't like configuration and/or ``.ini`` files,
+ could you do this yourself in Python code?
+
+#. Can we have multiple ``.ini`` configuration files for a project? Why
+ might you want to do that?
+
+#. The entry point in ``setup.py`` didn't mention ``__init__.py`` when
+ it the ``main`` function. Why not?
+
+.. seealso::
+ :ref:`project_narr`,
+ :ref:`scaffolding_chapter`,
+ :ref:`what_is_this_pserve_thing`,
+ :ref:`environment_chapter`,
+ :ref:`paste_chapter`
+
+Extra Credit
+============
+
+#. What is the purpose of ``**settings``? What does the ``**`` signify?
diff --git a/docs/quick_tutorial/ini/development.ini b/docs/quick_tutorial/ini/development.ini
new file mode 100644
index 000000000..ca7d9bf81
--- /dev/null
+++ b/docs/quick_tutorial/ini/development.ini
@@ -0,0 +1,38 @@
+[app:main]
+use = egg:tutorial
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/ini/setup.py b/docs/quick_tutorial/ini/setup.py
new file mode 100644
index 000000000..9997984d3
--- /dev/null
+++ b/docs/quick_tutorial/ini/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/ini/tutorial/__init__.py b/docs/quick_tutorial/ini/tutorial/__init__.py
new file mode 100644
index 000000000..2b4e84f30
--- /dev/null
+++ b/docs/quick_tutorial/ini/tutorial/__init__.py
@@ -0,0 +1,13 @@
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ return Response('<body><h1>Hello World!</h1></body>')
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/jinja2.rst b/docs/quick_tutorial/jinja2.rst
new file mode 100644
index 000000000..40d941098
--- /dev/null
+++ b/docs/quick_tutorial/jinja2.rst
@@ -0,0 +1,98 @@
+.. _qtut_jinja2:
+
+==============================
+12: Templating With ``jinja2``
+==============================
+
+We just said Pyramid doesn't prefer one templating language over
+another. Time to prove it. Jinja2 is a popular templating system,
+used in Flask and modelled after Django's templates. Let's add
+``pyramid_jinja2``, a Pyramid :term:`add-on` which enables Jinja2 as a
+:term:`renderer` in our Pyramid applications.
+
+Objectives
+==========
+
+- Show Pyramid's support for different templating systems
+
+- Learn about installing Pyramid add-ons
+
+Steps
+=====
+
+#. In this step let's start by installing the ``pyramid_jinja2``
+ add-on, the copying the ``view_class`` step's directory:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes jinja2; cd jinja2
+ $ $VENV/bin/python setup.py develop
+ $ $VENV/bin/easy_install pyramid_jinja2
+
+#. We need to add an item to ``pyramid.includes`` in
+ ``jinja2/development.ini``:
+
+ .. literalinclude:: jinja2/development.ini
+ :language: ini
+ :linenos:
+
+#. Our ``jinja2/tutorial/views.py`` simply changes its ``renderer``:
+
+ .. literalinclude:: jinja2/tutorial/views.py
+ :linenos:
+
+#. Add ``jinja2/tutorial/home.jinja2`` as a template:
+
+ .. literalinclude:: jinja2/tutorial/home.jinja2
+ :language: html
+
+#. Get the ``pyramid.includes`` into the functional test setup in
+ ``jinja2/tutorial/tests.py``:
+
+ .. literalinclude:: jinja2/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in your browser.
+
+Analysis
+========
+
+Getting a Pyramid add-on into Pyramid is simple. First you use normal
+Python package installation tools to install the add-on package into
+your Python. You then tell Pyramid's configurator to run the setup code
+in the add-on. In this case the setup code told Pyramid to make a new
+"renderer" available that looked for ``.jinja2`` file extensions.
+
+Our view code stayed largely the same. We simply changed the file
+extension on the renderer. For the template, the syntax for Chameleon
+and Jinja2's basic variable insertion is very similar.
+
+Our functional tests don't have ``development.ini`` so they needed the
+``pyramid.includes`` to be setup in the test setup.
+
+Extra Credit
+============
+
+#. Our project now depends on ``pyramid_jinja2``. We installed that
+ dependency manually. What is another way we could have made the
+ association?
+
+#. We used ``development.ini`` to get the :term:`configurator` to
+ load ``pyramid_jinja2``'s configuration. What is another way could
+ include it into the config?
+
+.. seealso:: `Jinja2 homepage <http://jinja.pocoo.org/>`_,
+ and
+ :ref:`pyramid_jinja2 Overview <jinja2:overview>`
diff --git a/docs/quick_tutorial/jinja2/development.ini b/docs/quick_tutorial/jinja2/development.ini
new file mode 100644
index 000000000..c096fa936
--- /dev/null
+++ b/docs/quick_tutorial/jinja2/development.ini
@@ -0,0 +1,42 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+ pyramid_jinja2
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/jinja2/setup.py b/docs/quick_tutorial/jinja2/setup.py
new file mode 100644
index 000000000..9997984d3
--- /dev/null
+++ b/docs/quick_tutorial/jinja2/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/jinja2/tutorial/__init__.py b/docs/quick_tutorial/jinja2/tutorial/__init__.py
new file mode 100644
index 000000000..013d4538f
--- /dev/null
+++ b/docs/quick_tutorial/jinja2/tutorial/__init__.py
@@ -0,0 +1,9 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/jinja2/tutorial/home.jinja2 b/docs/quick_tutorial/jinja2/tutorial/home.jinja2
new file mode 100644
index 000000000..975323169
--- /dev/null
+++ b/docs/quick_tutorial/jinja2/tutorial/home.jinja2
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: {{ name }}</title>
+</head>
+<body>
+<h1>Hi {{ name }}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/jinja2/tutorial/tests.py b/docs/quick_tutorial/jinja2/tutorial/tests.py
new file mode 100644
index 000000000..0b22946f3
--- /dev/null
+++ b/docs/quick_tutorial/jinja2/tutorial/tests.py
@@ -0,0 +1,50 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual('Home View', response['name'])
+
+ def test_hello(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.hello()
+ self.assertEqual('Hello View', response['name'])
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+
+ settings = {
+ 'pyramid.includes': [
+ 'pyramid_jinja2'
+ ]
+ }
+ app = main({}, **settings)
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hi Home View', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<h1>Hi Hello View', res.body)
diff --git a/docs/quick_tutorial/jinja2/tutorial/views.py b/docs/quick_tutorial/jinja2/tutorial/views.py
new file mode 100644
index 000000000..fa9ec5121
--- /dev/null
+++ b/docs/quick_tutorial/jinja2/tutorial/views.py
@@ -0,0 +1,18 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+@view_defaults(renderer='home.jinja2')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(route_name='home')
+ def home(self):
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello')
+ def hello(self):
+ return {'name': 'Hello View'}
diff --git a/docs/quick_tutorial/json.rst b/docs/quick_tutorial/json.rst
new file mode 100644
index 000000000..ece8a61c0
--- /dev/null
+++ b/docs/quick_tutorial/json.rst
@@ -0,0 +1,103 @@
+.. _qtut_json:
+
+========================================
+14: Ajax Development With JSON Renderers
+========================================
+
+Modern web apps are more than rendered HTML. Dynamic pages now use
+JavaScript to update the UI in the browser by requesting server data as
+JSON. Pyramid supports this with a *JSON renderer*.
+
+Background
+==========
+
+As we saw in :doc:`templating`, view declarations can specify a
+renderer. Output from the view is then run through the renderer,
+which generates and returns the ``Response``. We first used a Chameleon
+renderer, then a Jinja2 renderer.
+
+Renderers aren't limited, however, to templates that generate HTML.
+Pyramid supplies a JSON renderer which takes Python data,
+serializes it to JSON, and performs some other functions such as
+setting the content type. In fact, you can write your own renderer (or
+extend a built-in renderer) containing custom logic for your unique
+application.
+
+Steps
+=====
+
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes json; cd json
+ $ $VENV/bin/python setup.py develop
+
+#. We add a new route for ``hello_json`` in
+ ``json/tutorial/__init__.py``:
+
+ .. literalinclude:: json/tutorial/__init__.py
+ :linenos:
+
+#. Rather than implement a new view, we will "stack" another decorator
+ on the ``hello`` view:
+
+ .. literalinclude:: json/tutorial/views.py
+ :linenos:
+
+#. We need a new functional test at the end of
+ ``json/tutorial/tests.py``:
+
+ .. literalinclude:: json/tutorial/tests.py
+ :linenos:
+
+#. Run the tests:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/howdy.json in your browser and you
+ will see the resulting JSON response.
+
+Analysis
+========
+
+Earlier we changed our view functions and methods to return Python
+data. This change to a data-oriented view layer made test writing
+easier, decoupling the templating from the view logic.
+
+Since Pyramid has a JSON renderer as well as the templating renderers,
+it is an easy step to return JSON. In this case we kept the exact same
+view and arranged to return a JSON encoding of the view data. We did
+this by:
+
+- Adding a route to map ``/howdy.json`` to a route name
+
+- Providing a ``@view_config`` that associated that route name with an
+ existing view
+
+- *overriding* the view defaults in the view config that mentions the
+ ``hello_json`` route, so that when the route is matched, we use the JSON
+ renderer rather than the ``home.pt`` template renderer that would otherwise
+ be used.
+
+In fact, for pure Ajax-style web applications, we could re-use the existing
+route by using Pyramid's view predicates to match on the
+``Accepts:`` header sent by modern Ajax implementation.
+
+Pyramid's JSON renderer uses the base Python JSON encoder,
+thus inheriting its strengths and weaknesses. For example,
+Python can't natively JSON encode DateTime objects. There are a number
+of solutions for this in Pyramid, including extending the JSON renderer
+with a custom renderer.
+
+.. seealso:: :ref:`views_which_use_a_renderer`,
+ :ref:`json_renderer`, and
+ :ref:`adding_and_overriding_renderers`
diff --git a/docs/quick_tutorial/json/development.ini b/docs/quick_tutorial/json/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/json/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/json/setup.py b/docs/quick_tutorial/json/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/json/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/json/tutorial/__init__.py b/docs/quick_tutorial/json/tutorial/__init__.py
new file mode 100644
index 000000000..6652544c3
--- /dev/null
+++ b/docs/quick_tutorial/json/tutorial/__init__.py
@@ -0,0 +1,11 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.add_route('hello_json', 'howdy.json')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/json/tutorial/home.pt b/docs/quick_tutorial/json/tutorial/home.pt
new file mode 100644
index 000000000..a0cc08e7a
--- /dev/null
+++ b/docs/quick_tutorial/json/tutorial/home.pt
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>Hi ${name}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/json/tutorial/tests.py b/docs/quick_tutorial/json/tutorial/tests.py
new file mode 100644
index 000000000..c3cdacbdb
--- /dev/null
+++ b/docs/quick_tutorial/json/tutorial/tests.py
@@ -0,0 +1,50 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual('Home View', response['name'])
+
+ def test_hello(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.hello()
+ self.assertEqual('Hello View', response['name'])
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hi Home View', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<h1>Hi Hello View', res.body)
+
+ def test_hello_json(self):
+ res = self.testapp.get('/howdy.json', status=200)
+ self.assertIn(b'{"name": "Hello View"}', res.body)
+ self.assertEqual(res.content_type, 'application/json')
+
diff --git a/docs/quick_tutorial/json/tutorial/views.py b/docs/quick_tutorial/json/tutorial/views.py
new file mode 100644
index 000000000..f15e55d1b
--- /dev/null
+++ b/docs/quick_tutorial/json/tutorial/views.py
@@ -0,0 +1,19 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(route_name='home')
+ def home(self):
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello')
+ @view_config(route_name='hello_json', renderer='json')
+ def hello(self):
+ return {'name': 'Hello View'}
diff --git a/docs/quick_tutorial/logging.rst b/docs/quick_tutorial/logging.rst
new file mode 100644
index 000000000..0167e5249
--- /dev/null
+++ b/docs/quick_tutorial/logging.rst
@@ -0,0 +1,79 @@
+.. _qtut_logging:
+
+============================================
+16: Collecting Application Info With Logging
+============================================
+
+Capture debugging and error output from your web applications using
+standard Python logging.
+
+Background
+==========
+
+It's important to know what is going on inside our web application.
+In development we might need to collect some output. In production,
+we might need to detect problems when other people use the site. We
+need *logging*.
+
+Fortunately Pyramid uses the normal Python approach to logging. The
+scaffold generated, in your ``development.ini``, a number of lines that
+configure the logging for you to some reasonable defaults. You then see
+messages sent by Pyramid (for example, when a new request comes in.)
+
+Objectives
+==========
+
+- Inspect the configuration setup used for logging
+
+- Add logging statements to your view code
+
+Steps
+=====
+
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes logging; cd logging
+ $ $VENV/bin/python setup.py develop
+
+#. Extend ``logging/tutorial/views.py`` to log a message:
+
+ .. literalinclude:: logging/tutorial/views.py
+ :linenos:
+
+#. Make sure the tests still pass:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ and http://localhost:6543/howdy
+ in your browser. Note, both in the console and in the debug
+ toolbar, the message that you logged.
+
+Analysis
+========
+
+Our ``development.ini`` configuration file wires up Python standard
+logging for our Pyramid application:
+
+.. literalinclude:: logging/development.ini
+ :language: ini
+
+In this, our ``tutorial`` Python package is setup as a logger
+and configured to log messages at a ``DEBUG`` or higher level. When you
+visit http://localhost:6543 your console will now show::
+
+ 2013-08-09 10:42:42,968 DEBUG [tutorial.views][MainThread] In home view
+
+Also, if you have configured your Pyramid application to use the
+``pyramid_debugtoolbar``, logging statements appear in one of its menus.
+
+.. seealso:: See Also: :ref:`logging_chapter`
diff --git a/docs/quick_tutorial/logging/development.ini b/docs/quick_tutorial/logging/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/logging/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/logging/setup.py b/docs/quick_tutorial/logging/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/logging/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/logging/tutorial/__init__.py b/docs/quick_tutorial/logging/tutorial/__init__.py
new file mode 100644
index 000000000..c3e1c9eef
--- /dev/null
+++ b/docs/quick_tutorial/logging/tutorial/__init__.py
@@ -0,0 +1,10 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/logging/tutorial/home.pt b/docs/quick_tutorial/logging/tutorial/home.pt
new file mode 100644
index 000000000..a0cc08e7a
--- /dev/null
+++ b/docs/quick_tutorial/logging/tutorial/home.pt
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>Hi ${name}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/logging/tutorial/tests.py b/docs/quick_tutorial/logging/tutorial/tests.py
new file mode 100644
index 000000000..4381235ec
--- /dev/null
+++ b/docs/quick_tutorial/logging/tutorial/tests.py
@@ -0,0 +1,44 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual('Home View', response['name'])
+
+ def test_hello(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.hello()
+ self.assertEqual('Hello View', response['name'])
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hi Home View', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<h1>Hi Hello View', res.body)
diff --git a/docs/quick_tutorial/logging/tutorial/views.py b/docs/quick_tutorial/logging/tutorial/views.py
new file mode 100644
index 000000000..63d95f405
--- /dev/null
+++ b/docs/quick_tutorial/logging/tutorial/views.py
@@ -0,0 +1,23 @@
+import logging
+log = logging.getLogger(__name__)
+
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(route_name='home')
+ def home(self):
+ log.debug('In home view')
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello')
+ def hello(self):
+ log.debug('In hello view')
+ return {'name': 'Hello View'}
diff --git a/docs/quick_tutorial/more_view_classes.rst b/docs/quick_tutorial/more_view_classes.rst
new file mode 100644
index 000000000..2792869ac
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes.rst
@@ -0,0 +1,182 @@
+.. _qtut_more_view_classes:
+
+==========================
+15: More With View Classes
+==========================
+
+Group views into a class, sharing configuration, state, and logic.
+
+Background
+==========
+
+As part of its mission to help build more ambitious web applications,
+Pyramid provides many more features for views and view classes.
+
+The Pyramid documentation discusses views as a Python "callable". This
+callable can be a function, an object with an ``__call__``,
+or a Python class. In this last case, methods on the class can be
+decorated with ``@view_config`` to register the class methods with the
+:term:`configurator` as a view.
+
+So far our views have been simple, free-standing functions. Many times
+your views are related: different ways to look at or work on the same
+data or a REST API that handles multiple operations. Grouping these
+together as a
+:ref:`view class <class_as_view>` makes sense:
+
+- Group views
+
+- Centralize some repetitive defaults
+
+- Share some state and helpers
+
+Pyramid views have
+:ref:`view predicates <view_configuration_parameters>` that
+help determine which view is matched to a request. These predicates
+provide many axes of flexibility.
+
+The following shows a simple example with four operations operations:
+view a home page which leads to a form, save a change,
+and press the delete button.
+
+Objectives
+==========
+
+- Group related views into a view class
+
+- Centralize configuration with class-level ``@view_defaults``
+
+- Dispatch one route/URL to multiple views based on request data
+
+- Share stated and logic between views and templates via the view class
+
+Steps
+=====
+
+#. First we copy the results of the previous step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r templating more_view_classes; cd more_view_classes
+ $ $VENV/bin/python setup.py develop
+
+#. Our route in ``more_view_classes/tutorial/__init__.py`` needs some
+ replacement patterns:
+
+ .. literalinclude:: more_view_classes/tutorial/__init__.py
+ :linenos:
+
+#. Our ``more_view_classes/tutorial/views.py`` now has a view class with
+ several views:
+
+ .. literalinclude:: more_view_classes/tutorial/views.py
+ :linenos:
+
+#. Our primary view needs a template at
+ ``more_view_classes/tutorial/home.pt``:
+
+ .. literalinclude:: more_view_classes/tutorial/home.pt
+ :language: html
+
+#. Ditto for our other view from the previous section at
+ ``more_view_classes/tutorial/hello.pt``:
+
+ .. literalinclude:: more_view_classes/tutorial/hello.pt
+ :language: html
+
+#. We have an edit view that also needs a template at
+ ``more_view_classes/tutorial/edit.pt``:
+
+ .. literalinclude:: more_view_classes/tutorial/edit.pt
+ :language: html
+
+#. And finally the delete view's template at
+ ``more_view_classes/tutorial/delete.pt``:
+
+ .. literalinclude:: more_view_classes/tutorial/delete.pt
+ :language: html
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/howdy/jane/doe in your browser. Click
+ the ``Save`` and ``Delete`` buttons and watch the output in the
+ console window.
+
+Analysis
+========
+
+As you can see, the four views are logically grouped together.
+Specifically:
+
+- We have a ``home`` view available at http://localhost:6543/ with
+ a clickable link to the ``hello`` view.
+
+- The second view is returned when you go to ``/howdy/jane/doe``. This
+ URL is
+ mapped to the ``hello`` route that we centrally set using the optional
+ ``@view_defaults``.
+
+- The third view is returned when the form is submitted with a ``POST``
+ method. This rule is specified in the ``@view_config`` for that view.
+
+- The fourth view is returned when clicking on a button such
+ as ``<input type="submit" name="form.delete" value="Delete"/>``.
+
+In this step we show using the following information as criteria to
+decide which view to use:
+
+- Method of the HTTP request (``GET``, ``POST``, etc.)
+
+- Parameter information in the request (submitted form field names)
+
+We also centralize part of the view configuration to the class level
+with ``@view_defaults``, then in one view, override that default just
+for that one view. Finally, we put this commonality between views to
+work in the view class by sharing:
+
+- State assigned in ``TutorialViews.__init__``
+
+- A computed value
+
+These are then available both in the view methods but also in the
+templates (e.g. ``${view.view_name}`` and ``${view.full_name}``.
+
+As a note, we made a switch in our templates on how we generate URLs.
+We previously hardcode the URLs, such as::
+
+ <a href="/howdy/jane/doe">Howdy</a>
+
+In ``home.pt`` we switched to::
+
+ <a href="${request.route_url('hello', first='jane',
+ last='doe')}">form</a>
+
+Pyramid has rich facilities to help generate URLs in a flexible,
+non-error-prone fashion.
+
+Extra Credit
+============
+
+#. Why could our template do ``${view.full_name}`` and not have to do
+ ``${view.full_name()}``?
+
+#. The ``edit`` and ``delete`` views are both submitted to with
+ ``POST``. Why does the ``edit`` view configuration not catch the
+ the ``POST`` used by ``delete``?
+
+#. We used Python ``@property`` on ``full_name``. If we reference this
+ many times in a template or view code, it would re-compute this
+ every time. Does Pyramid provide something that will cache the initial
+ computation on a property?
+
+#. Can you associate more than one route with the same view?
+
+#. There is also a ``request.route_path`` API. How does this differ from
+ ``request.route_url``?
+
+.. seealso:: :ref:`class_as_view`, `Weird Stuff You Can Do With
+ URL Dispatch <http://www.plope.com/weird_pyramid_urldispatch>`_
diff --git a/docs/quick_tutorial/more_view_classes/development.ini b/docs/quick_tutorial/more_view_classes/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/more_view_classes/setup.py b/docs/quick_tutorial/more_view_classes/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/__init__.py b/docs/quick_tutorial/more_view_classes/tutorial/__init__.py
new file mode 100644
index 000000000..9c1bcec06
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/tutorial/__init__.py
@@ -0,0 +1,10 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy/{first}/{last}')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/delete.pt b/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
new file mode 100644
index 000000000..67cc8bf09
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/tutorial/delete.pt
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${page_title}</title>
+</head>
+<body>
+<h1>${view.view_name} - ${page_title}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/edit.pt b/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
new file mode 100644
index 000000000..1bd204065
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/tutorial/edit.pt
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+</head>
+<body>
+<h1>${view.view_name} - ${page_title}</h1>
+<p>You submitted <code>${new_name}</code></p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/hello.pt b/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
new file mode 100644
index 000000000..8a39aed09
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/tutorial/hello.pt
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+</head>
+<body>
+<h1>${view.view_name} - ${page_title}</h1>
+<p>Welcome, ${view.full_name}</p>
+<form method="POST"
+ action="${request.current_route_url()}">
+ <input name="new_name"/>
+ <input type="submit" name="form.edit" value="Save"/>
+ <input type="submit" name="form.delete" value="Delete"/>
+</form>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/home.pt b/docs/quick_tutorial/more_view_classes/tutorial/home.pt
new file mode 100644
index 000000000..fa9016705
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/tutorial/home.pt
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${view.view_name} - ${page_title}</title>
+</head>
+<body>
+<h1>${view.view_name} - ${page_title}</h1>
+
+<p>Go to the <a href="${request.route_url('hello', first='jane',
+ last='doe')}">form</a>.</p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/tests.py b/docs/quick_tutorial/more_view_classes/tutorial/tests.py
new file mode 100644
index 000000000..dca8d7f7b
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/tutorial/tests.py
@@ -0,0 +1,31 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual('Home View', response['page_title'])
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'TutorialViews - Home View', res.body)
diff --git a/docs/quick_tutorial/more_view_classes/tutorial/views.py b/docs/quick_tutorial/more_view_classes/tutorial/views.py
new file mode 100644
index 000000000..fdba04ba8
--- /dev/null
+++ b/docs/quick_tutorial/more_view_classes/tutorial/views.py
@@ -0,0 +1,39 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+@view_defaults(route_name='hello')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+ self.view_name = 'TutorialViews'
+
+ @property
+ def full_name(self):
+ first = self.request.matchdict['first']
+ last = self.request.matchdict['last']
+ return first + ' ' + last
+
+ @view_config(route_name='home', renderer='home.pt')
+ def home(self):
+ return {'page_title': 'Home View'}
+
+
+ # Retrieving /howdy/first/last the first time
+ @view_config(renderer='hello.pt')
+ def hello(self):
+ return {'page_title': 'Hello View'}
+
+ # Posting to /home via the "Edit" submit button
+ @view_config(request_method='POST', renderer='edit.pt')
+ def edit(self):
+ new_name = self.request.params['new_name']
+ return {'page_title': 'Edit View', 'new_name': new_name}
+
+ # Posting to /home via the "Delete" submit button
+ @view_config(request_param='form.delete', renderer='delete.pt')
+ def delete(self):
+ print ('Deleted')
+ return {'page_title': 'Delete View'}
diff --git a/docs/quick_tutorial/package.rst b/docs/quick_tutorial/package.rst
new file mode 100644
index 000000000..90d022b29
--- /dev/null
+++ b/docs/quick_tutorial/package.rst
@@ -0,0 +1,112 @@
+============================================
+02: Python Packages for Pyramid Applications
+============================================
+
+Most modern Python development is done using Python packages, an approach
+Pyramid puts to good use. In this step we re-do "Hello World" as a
+minimum Python package inside a minimum Python project.
+
+Background
+==========
+
+Python developers can organize a collection of modules and files into a
+namespaced unit called a :ref:`package <python:tut-packages>`. If a
+directory is on ``sys.path`` and has a special file named
+``__init__.py``, it is treated as a Python package.
+
+Packages can be bundled up, made available for installation,
+and installed through a (muddled, but improving) toolchain oriented
+around a ``setup.py`` file for a
+`setuptools project <http://pythonhosted.org/setuptools/setuptools.html>`_.
+Explaining it all in this
+tutorial will induce madness. For this tutorial, this is all you need to
+know:
+
+- We will have a directory for each tutorial step as a
+ setuptools *project*
+
+- This project will contain a ``setup.py`` which injects the features
+ of the setuptool's project machinery into the directory
+
+- In this project we will make a ``tutorial`` subdirectory into a Python
+ *package* using an ``__init__.py`` Python module file
+
+- We will run ``python setup.py develop`` to install our project in
+ development mode
+
+In summary:
+
+- You'll do your development in a Python *package*
+
+- That package will be part of a setuptools *project*
+
+Objectives
+==========
+
+- Make a Python "package" directory with an ``__init__.py``
+
+- Get a minimum Python "project" in place by making a ``setup.py``
+
+- Install our ``tutorial`` project in development mode
+
+Steps
+=====
+
+#. Make an area for this tutorial step:
+
+ .. code-block:: bash
+
+ $ cd ..; mkdir package; cd package
+
+#. In ``package/setup.py``, enter the following:
+
+ .. literalinclude:: package/setup.py
+
+#. Make the new project installed for development then make a directory
+ for the actual code:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/python setup.py develop
+ $ mkdir tutorial
+
+#. Enter the following into ``package/tutorial/__init__.py``:
+
+ .. literalinclude:: package/tutorial/__init__.py
+
+#. Enter the following into ``package/tutorial/app.py``:
+
+ .. literalinclude:: package/tutorial/app.py
+
+#. Run the WSGI application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/python tutorial/app.py
+
+#. Open http://localhost:6543/ in your browser.
+
+Analysis
+========
+
+Python packages give us an organized unit of project development.
+Python projects, via ``setup.py``, gives us special features when
+our package is installed (in this case, in local development mode.)
+
+In this step we have a Python package called ``tutorial``. We use the
+same name in each step of the tutorial, to avoid unnecessary re-typing.
+
+Above this ``tutorial`` directory we have the files that handle the
+packaging of this, well, package. At the moment, all we need is a
+bare-bones ``ini/setup.py``.
+
+Everything else is the same about our application. We simply made a
+Python package with a ``setup.py`` and installed it in development mode.
+
+Note that the way we're running the app (``python tutorial/app.py``) is a bit
+of an odd duck. We would never do this unless we were writing a tutorial that
+tries to capture how this stuff works a step at a time. It's generally a bad
+idea to run a Python module inside a package directly as a script.
+
+.. seealso:: :ref:`Python Packages <python:tut-packages>`,
+ `setuptools Entry Points <http://pythonhosted.org/setuptools/pkg_resources.html#entry-points>`_
diff --git a/docs/quick_tutorial/package/setup.py b/docs/quick_tutorial/package/setup.py
new file mode 100644
index 000000000..bcfcfa684
--- /dev/null
+++ b/docs/quick_tutorial/package/setup.py
@@ -0,0 +1,9 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/package/tutorial/__init__.py b/docs/quick_tutorial/package/tutorial/__init__.py
new file mode 100644
index 000000000..d310fdde9
--- /dev/null
+++ b/docs/quick_tutorial/package/tutorial/__init__.py
@@ -0,0 +1 @@
+# package \ No newline at end of file
diff --git a/docs/quick_tutorial/package/tutorial/app.py b/docs/quick_tutorial/package/tutorial/app.py
new file mode 100644
index 000000000..210075023
--- /dev/null
+++ b/docs/quick_tutorial/package/tutorial/app.py
@@ -0,0 +1,17 @@
+from wsgiref.simple_server import make_server
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ print ('Incoming request')
+ return Response('<body><h1>Hello World!</h1></body>')
+
+
+if __name__ == '__main__':
+ config = Configurator()
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ app = config.make_wsgi_app()
+ server = make_server('0.0.0.0', 6543, app)
+ server.serve_forever() \ No newline at end of file
diff --git a/docs/quick_tutorial/request_response.rst b/docs/quick_tutorial/request_response.rst
new file mode 100644
index 000000000..504803804
--- /dev/null
+++ b/docs/quick_tutorial/request_response.rst
@@ -0,0 +1,103 @@
+.. _qtut_request_response:
+
+=======================================
+10: Handling Web Requests and Responses
+=======================================
+
+Web applications handle incoming requests and return outgoing responses.
+Pyramid makes working with requests and responses convenient and
+reliable.
+
+Objectives
+==========
+
+- Learn the background on Pyramid's choices for requests and responses
+
+- Grab data out of the request
+
+- Change information in the response headers
+
+Background
+==========
+
+Developing for the web means processing web requests. As this is a
+critical part of a web application, web developers need a robust,
+mature set of software for web requests and returning web
+responses.
+
+Pyramid has always fit nicely into the existing world of Python web
+development (virtual environments, packaging, scaffolding,
+first to embrace Python 3, etc.) For request handling, Pyramid turned
+to the well-regarded :term:`WebOb` Python library for request and
+response handling. In our example
+above, Pyramid hands ``hello_world`` a ``request`` that is
+:ref:`based on WebOb <webob_chapter>`.
+
+Steps
+=====
+
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes request_response; cd request_response
+ $ $VENV/bin/python setup.py develop
+
+#. Simplify the routes in ``request_response/tutorial/__init__.py``:
+
+ .. literalinclude:: request_response/tutorial/__init__.py
+
+#. We only need one view in ``request_response/tutorial/views.py``:
+
+ .. literalinclude:: request_response/tutorial/views.py
+
+#. Update the tests in ``request_response/tutorial/tests.py``:
+
+ .. literalinclude:: request_response/tutorial/tests.py
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in your browser. You will be
+ redirected to http://localhost:6543/plain
+
+#. Open http://localhost:6543/plain?name=alice in your browser.
+
+Analysis
+========
+
+In this view class we have two routes and two views, with the first
+leading to the second by an HTTP redirect. Pyramid can
+:ref:`generate redirects <http_redirect>` by returning a
+special object from a view or raising a special exception.
+
+In this Pyramid view, we get the URL being visited from ``request.url``.
+Also, if you visited http://localhost:6543/plain?name=alice,
+the name is included in the body of the response::
+
+ URL http://localhost:6543/plain?name=alice with name: alice
+
+Finally, we set the response's content type and body, then return the
+Response.
+
+We updated the unit and functional tests to prove that our code
+does the redirection, but also handles sending and not sending
+``/plain?name``.
+
+Extra Credit
+============
+
+#. Could we also ``raise HTTPFound(location='/plain')`` instead of
+ returning it? If so, what's the difference?
+
+.. seealso:: :ref:`webob_chapter`,
+ :ref:`generate redirects <http_redirect>`
diff --git a/docs/quick_tutorial/request_response/development.ini b/docs/quick_tutorial/request_response/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/request_response/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/request_response/setup.py b/docs/quick_tutorial/request_response/setup.py
new file mode 100644
index 000000000..9997984d3
--- /dev/null
+++ b/docs/quick_tutorial/request_response/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/request_response/tutorial/__init__.py b/docs/quick_tutorial/request_response/tutorial/__init__.py
new file mode 100644
index 000000000..77a172888
--- /dev/null
+++ b/docs/quick_tutorial/request_response/tutorial/__init__.py
@@ -0,0 +1,9 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.add_route('home', '/')
+ config.add_route('plain', '/plain')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/request_response/tutorial/tests.py b/docs/quick_tutorial/request_response/tutorial/tests.py
new file mode 100644
index 000000000..7486c2b2d
--- /dev/null
+++ b/docs/quick_tutorial/request_response/tutorial/tests.py
@@ -0,0 +1,54 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual(response.status, '302 Found')
+
+ def test_plain_without_name(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.plain()
+ self.assertIn(b'No Name Provided', response.body)
+
+ def test_plain_with_name(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ request.GET['name'] = 'Jane Doe'
+ inst = TutorialViews(request)
+ response = inst.plain()
+ self.assertIn(b'Jane Doe', response.body)
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_plain_without_name(self):
+ res = self.testapp.get('/plain', status=200)
+ self.assertIn(b'No Name Provided', res.body)
+
+ def test_plain_with_name(self):
+ res = self.testapp.get('/plain?name=Jane%20Doe', status=200)
+ self.assertIn(b'Jane Doe', res.body)
diff --git a/docs/quick_tutorial/request_response/tutorial/views.py b/docs/quick_tutorial/request_response/tutorial/views.py
new file mode 100644
index 000000000..8c7ff5f37
--- /dev/null
+++ b/docs/quick_tutorial/request_response/tutorial/views.py
@@ -0,0 +1,22 @@
+from pyramid.httpexceptions import HTTPFound
+from pyramid.response import Response
+from pyramid.view import view_config
+
+
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(route_name='home')
+ def home(self):
+ return HTTPFound(location='/plain')
+
+ @view_config(route_name='plain')
+ def plain(self):
+ name = self.request.params.get('name', 'No Name Provided')
+
+ body = 'URL %s with name: %s' % (self.request.url, name)
+ return Response(
+ content_type='text/plain',
+ body=body
+ )
diff --git a/docs/quick_tutorial/requirements.rst b/docs/quick_tutorial/requirements.rst
new file mode 100644
index 000000000..40e818807
--- /dev/null
+++ b/docs/quick_tutorial/requirements.rst
@@ -0,0 +1,250 @@
+.. _qtut_requirements:
+
+============
+Requirements
+============
+
+Let's get our tutorial environment setup. Most of the setup work is in
+standard Python development practices (install Python,
+make an isolated environment, and setup packaging tools.)
+
+.. note::
+
+ Pyramid encourages standard Python development practices with
+ packaging tools, virtual environments, logging, and so on. There
+ are many variations, implementations, and opinions across the Python
+ community. For consistency, ease of documentation maintenance,
+ and to minimize confusion, the Pyramid *documentation* has adopted
+ specific conventions.
+
+This *Quick Tutorial* is based on:
+
+* **Python 3.3**. Pyramid fully supports Python 3.2+ and Python 2.6+.
+ This tutorial uses **Python 3.3** but runs fine under Python 2.7.
+
+* **pyvenv**. We believe in virtual environments. For this tutorial,
+ we use Python 3.3's built-in solution, the ``pyvenv`` command.
+ For Python 2.7, you can install ``virtualenv``.
+
+* **setuptools and easy_install**. We use
+ `setuptools <https://pypi.python.org/pypi/setuptools/>`_
+ and its ``easy_install`` for package management.
+
+* **Workspaces, projects, and packages.** Our home directory
+ will contain a *tutorial workspace* with our Python virtual
+ environment(s) and *Python projects* (a directory with packaging
+ information and *Python packages* of working code.)
+
+* **Unix commands**. Commands in this tutorial use UNIX syntax and
+ paths. Windows users should adjust commands accordingly.
+
+.. note::
+
+ Pyramid was one of the first web frameworks to fully support Python 3 in
+ October 2011.
+
+Steps
+=====
+
+#. :ref:`install-python-3.3-or-greater`
+#. :ref:`create-a-project-directory-structure`
+#. :ref:`set-an-environment-variable`
+#. :ref:`create-a-virtual-environment`
+#. :ref:`install-setuptools-(python-packaging-tools)`
+#. :ref:`install-pyramid`
+
+.. _install-python-3.3-or-greater:
+
+Install Python 3.3 or greater
+-----------------------------
+
+Download the latest standard Python 3.3+ release (not development
+release) from
+`python.org <http://www.python.org/download/releases/>`_. On that page, you
+must click the latest version, then scroll down to the "Downloads" section
+for your operating system.
+
+Windows and Mac OS X users can download and run an installer.
+
+Windows users should also install the `Python for Windows extensions
+<http://sourceforge.net/projects/pywin32/files/pywin32/>`_. Carefully read the
+``README.txt`` file at the end of the list of builds, and follow its
+directions. Make sure you get the proper 32- or 64-bit build and Python
+version.
+
+Linux users can either use their package manager to install Python 3.3
+or may
+`build Python 3.3 from source
+<http://pyramid.readthedocs.org/en/master/narr/install.html#package-manager-
+method>`_.
+
+
+.. _create-a-project-directory-structure:
+
+Create a project directory structure
+------------------------------------
+
+We will arrive at a directory structure of
+``workspace->project->package``, with our workspace named
+``quick_tutorial``. The following diagram shows how this is structured
+and where our virtual environment will reside:
+
+.. figure:: ../_static/directory_structure_pyramid.png
+ :alt: Final directory structure
+
+ Final directory structure.
+
+For Linux, the commands to do so are as follows:
+
+.. code-block:: bash
+
+ # Mac and Linux
+ $ cd ~
+ $ mkdir -p projects/quick_tutorial
+ $ cd projects/quick_tutorial
+
+For Windows:
+
+.. code-block:: posh
+
+ # Windows
+ c:\> cd \
+ c:\> mkdir projects\quick_tutorial
+ c:\> cd projects\quick_tutorial
+
+In the above figure, your user home directory is represented by ``~``. In
+your home directory, all of your projects are in the ``projects`` directory.
+This is a general convention not specific to Pyramid that many developers use.
+Windows users will do well to use ``c:\`` as the location for ``projects`` in
+order to avoid spaces in any of the path names.
+
+Next within ``projects`` is your workspace directory, here named
+``quick_tutorial``. A workspace is a common term used by integrated
+development environments (IDE) like PyCharm and PyDev that stores
+isolated Python environments (virtualenvs) and specific project files
+and repositories.
+
+
+.. _set-an-environment-variable:
+
+Set an Environment Variable
+---------------------------
+
+This tutorial will refer frequently to the location of the virtual
+environment. We set an environment variable to save typing later.
+
+.. code-block:: bash
+
+ # Mac and Linux
+ $ export VENV=~/projects/quick_tutorial/env33/
+
+ # Windows
+ # TODO: This command does not work
+ c:\> set VENV=c:\projects\quick_tutorial\env33
+
+
+.. _create-a-virtual-environment:
+
+Create a Virtual Environment
+----------------------------
+
+.. warning:: The current state of isolated Python environments using
+ ``pyvenv`` on Windows is suboptimal in comparison to Mac and Linux. See
+ http://stackoverflow.com/q/15981111/95735 for a discussion of the issue
+ and `PEP 453 <http://www.python.org/dev/peps/pep-0453/>`_ for a proposed
+ resolution.
+
+``pyvenv`` is a tool to create isolated Python 3.3 environments, each
+with its own Python binary and independent set of installed Python
+packages in its site directories. Let's create one, using the location
+we just specified in the environment variable.
+
+.. code-block:: bash
+
+ # Mac and Linux
+ $ pyvenv $VENV
+
+ # Windows
+ c:\> c:\Python33\python -m venv %VENV%
+
+.. seealso:: See also Python 3's :mod:`venv module <python3:venv>`,
+ Python 2's `virtualenv <http://www.virtualenv.org/en/latest/>`_
+ package,
+ :ref:`Installing Pyramid on a Windows System <installing_windows>`
+
+
+.. _install-setuptools-(python-packaging-tools):
+
+Install ``setuptools`` (Python packaging tools)
+-----------------------------------------------
+
+The following command will download a script to install ``setuptools``, then
+pipe it to your environment's version of Python.
+
+.. code-block:: bash
+
+ # Mac and Linux
+ $ wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | $VENV/bin/python
+
+ # Windows
+ # Use your browser to download:
+ # https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.p
+ # ...into c:\projects\quick_tutorial\ez_setup.py
+ c:\> %VENV%\Scripts\python ez_setup.py
+
+If ``wget`` complains with a certificate error, then run this command instead:
+
+.. code-block:: bash
+
+ # Mac and Linux
+ $ wget --no-check-certificate https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | $VENV/bin/python
+
+
+.. _install-pyramid:
+
+Install Pyramid
+---------------
+
+We have our Python standard prerequisites out of the way. The Pyramid
+part is pretty easy:
+
+.. parsed-literal::
+
+ # Mac and Linux
+ $ $VENV/bin/easy_install "pyramid==\ |release|\ "
+
+ # Windows
+ c:\\> %VENV%\\Scripts\\easy_install "pyramid==\ |release|\ "
+
+Our Python virtual environment now has the Pyramid software available.
+
+You can optionally install some of the extra Python packages used
+during this tutorial:
+
+.. code-block:: bash
+
+ # Mac and Linux
+ $ $VENV/bin/easy_install nose webtest deform sqlalchemy \
+ pyramid_chameleon pyramid_debugtoolbar waitress \
+ pyramid_jinja2 pyramid_tm zope.sqlalchemy pysqlite
+
+ # Windows
+ c:\> %VENV%\Scripts\easy_install nose webtest deform sqlalchemy pyramid_chameleon
+
+
+
+.. note::
+
+ Why ``easy_install`` and not ``pip``? Pyramid encourages use of namespace
+ packages which, until recently, ``pip`` didn't permit. Also, Pyramid has
+ some optional C extensions for performance. With ``easy_install``, Windows
+ users can get these extensions without needing a C compiler.
+
+.. seealso:: See Also: :ref:`installing_unix`. For instructions to set up your
+ Python environment for development using Windows or Python 2, see Pyramid's
+ :ref:`Before You Install <installing_chapter>`.
+
+ See also Python 3's :mod:`venv module <python3:venv>`, the `setuptools` `installation instructions
+ <https://pypi.python.org/pypi/setuptools/0.9.8#installation-instructions>`_,
+ and `easy_install help <https://pypi.python.org/pypi/setuptools/0.9.8#using-setuptools-and-easyinstall>`_.
+
diff --git a/docs/quick_tutorial/routing.rst b/docs/quick_tutorial/routing.rst
new file mode 100644
index 000000000..54dff5c39
--- /dev/null
+++ b/docs/quick_tutorial/routing.rst
@@ -0,0 +1,121 @@
+.. _qtut_routing:
+
+==========================================
+11: Dispatching URLs To Views With Routing
+==========================================
+
+Routing matches incoming URL patterns to view code. Pyramid's routing
+has a number of useful features.
+
+Background
+==========
+
+Writing web applications usually means sophisticated URL design. We
+just saw some Pyramid machinery for requests and views. Let's look at
+features that help in routing.
+
+Previously we saw the basics of routing URLs to views in
+
+- Your project's "setup" code registers a route name to be used when
+ matching part of the URL
+
+- Elsewhere, a view is configured to be called for that route name
+
+.. note::
+
+ Why do this twice? Other Python web frameworks let you create a
+ route and associate it with a view in one step. As
+ illustrated in :ref:`routes_need_ordering`, multiple routes might match the
+ same URL pattern. Rather than provide ways to help guess, Pyramid lets you
+ be explicit in ordering. Pyramid also gives facilities to avoid the
+ problem. It's relatively easy to build a system that uses implicit route
+ ordering with Pyramid too. See `The Groundhog series of screencasts
+ <http://bfg.repoze.org/videos#groundhog1>`_ if you're interested in
+ doing so.
+
+Objectives
+==========
+
+- Define a route that extracts part of the URL into a Python dictionary
+
+- Use that dictionary data in a view
+
+Steps
+=====
+
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes routing; cd routing
+ $ $VENV/bin/python setup.py develop
+
+#. Our ``routing/tutorial/__init__.py`` needs a route with a replacement
+ pattern:
+
+ .. literalinclude:: routing/tutorial/__init__.py
+ :linenos:
+
+#. We just need one view in ``routing/tutorial/views.py``:
+
+ .. literalinclude:: routing/tutorial/views.py
+ :linenos:
+
+#. We just need one view in ``routing/tutorial/home.pt``:
+
+ .. literalinclude:: routing/tutorial/home.pt
+ :language: html
+ :linenos:
+
+#. Update ``routing/tutorial/tests.py``:
+
+ .. literalinclude:: routing/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/howdy/amy/smith in your browser.
+
+Analysis
+========
+
+In ``__init__.py`` we see an important change in our route declaration:
+
+.. code-block:: python
+
+ config.add_route('hello', '/howdy/{first}/{last}')
+
+With this we tell the :term:`configurator` that our URL has
+a "replacement pattern". With this, URLs such as ``/howdy/amy/smith``
+will assign ``amy`` to ``first`` and ``smith`` to ``last``. We can then
+use this data in our view:
+
+.. code-block:: python
+
+ self.request.matchdict['first']
+ self.request.matchdict['last']
+
+``request.matchdict`` contains values from the URL that match the
+"replacement patterns" (the curly braces) in the route declaration.
+This information can then be used anywhere in Pyramid that has access
+to the request.
+
+Extra Credit
+============
+
+#. What happens if you to go the URL
+ http://localhost:6543/howdy? Is this the result that you
+ expected?
+
+.. seealso:: `Weird Stuff You Can Do With URL
+ Dispatch <http://www.plope.com/weird_pyramid_urldispatch>`_
diff --git a/docs/quick_tutorial/routing/development.ini b/docs/quick_tutorial/routing/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/routing/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/routing/setup.py b/docs/quick_tutorial/routing/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/routing/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/routing/tutorial/__init__.py b/docs/quick_tutorial/routing/tutorial/__init__.py
new file mode 100644
index 000000000..4b2dac36d
--- /dev/null
+++ b/docs/quick_tutorial/routing/tutorial/__init__.py
@@ -0,0 +1,9 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/howdy/{first}/{last}')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/routing/tutorial/home.pt b/docs/quick_tutorial/routing/tutorial/home.pt
new file mode 100644
index 000000000..f2b991059
--- /dev/null
+++ b/docs/quick_tutorial/routing/tutorial/home.pt
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>${name}</h1>
+<p>First: ${first}, Last: ${last}</p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/routing/tutorial/tests.py b/docs/quick_tutorial/routing/tutorial/tests.py
new file mode 100644
index 000000000..572f389fb
--- /dev/null
+++ b/docs/quick_tutorial/routing/tutorial/tests.py
@@ -0,0 +1,36 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ request.matchdict['first'] = 'First'
+ request.matchdict['last'] = 'Last'
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual(response['first'], 'First')
+ self.assertEqual(response['last'], 'Last')
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/howdy/Jane/Doe', status=200)
+ self.assertIn(b'Jane', res.body)
+ self.assertIn(b'Doe', res.body)
diff --git a/docs/quick_tutorial/routing/tutorial/views.py b/docs/quick_tutorial/routing/tutorial/views.py
new file mode 100644
index 000000000..8a9211e92
--- /dev/null
+++ b/docs/quick_tutorial/routing/tutorial/views.py
@@ -0,0 +1,20 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(route_name='home')
+ def home(self):
+ first = self.request.matchdict['first']
+ last = self.request.matchdict['last']
+ return {
+ 'name': 'Home View',
+ 'first': first,
+ 'last': last
+ }
diff --git a/docs/quick_tutorial/scaffolds.rst b/docs/quick_tutorial/scaffolds.rst
new file mode 100644
index 000000000..8ca2d27df
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds.rst
@@ -0,0 +1,86 @@
+.. _qtut_scaffolds:
+
+=============================================
+Prelude: Quick Project Startup with Scaffolds
+=============================================
+
+To ease the process of getting started, Pyramid provides *scaffolds*
+that generate sample projects from templates in Pyramid and Pyramid
+add-ons.
+
+Background
+==========
+
+We're going to cover a lot in this tutorial, focusing on one topic at a
+time and writing everything from scratch. As a warmup, though,
+it sure would be nice to see some pixels on a screen.
+
+Like other web development frameworks, Pyramid provides a number of
+"scaffolds" that generate working Python, template, and CSS code for
+sample applications. In this step we'll use a built-in scaffold to let
+us preview a Pyramid application, before starting from scratch on Step 1.
+
+Objectives
+==========
+
+- Use Pyramid's ``pcreate`` command to list scaffolds and make a new
+ project
+
+- Start up a Pyramid application and visit it in a web browser
+
+Steps
+=====
+
+#. Pyramid's ``pcreate`` command can list the available scaffolds:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pcreate --list
+ Available scaffolds:
+ alchemy: Pyramid SQLAlchemy project using url dispatch
+ starter: Pyramid starter project
+ zodb: Pyramid ZODB project using traversal
+
+#. Tell ``pcreate`` to use the ``starter`` scaffold to make our project:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pcreate --scaffold starter scaffolds
+
+#. Use normal Python development to setup our project for development:
+
+ .. code-block:: bash
+
+ $ cd scaffolds
+ $ $VENV/bin/python setup.py develop
+
+#. Startup the application by pointing Pyramid's ``pserve`` command at
+ the project's (generated) configuration file:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+ On startup, ``pserve`` logs some output:
+
+ .. code-block:: bash
+
+ Starting subprocess with file monitor
+ Starting server in PID 72213.
+ Starting HTTP server on http://0.0.0.0:6543
+
+#. Open http://localhost:6543/ in your browser.
+
+Analysis
+========
+
+Rather than starting from scratch, ``pcreate`` can make getting a
+Python project containing a Pyramid application a quick matter.
+Pyramid ships with a few scaffolds. But installing a Pyramid add-on can
+give you new scaffolds from that add-on.
+
+``pserve`` is Pyramid's application runner, separating operational
+details from your code. When you install Pyramid, a small command
+program called ``pserve`` is written to your ``bin`` directory. This
+program is an executable Python module. It is passed a configuration
+file (in this case, ``development.ini``.)
diff --git a/docs/quick_tutorial/scaffolds/CHANGES.txt b/docs/quick_tutorial/scaffolds/CHANGES.txt
new file mode 100644
index 000000000..35a34f332
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/CHANGES.txt
@@ -0,0 +1,4 @@
+0.0
+---
+
+- Initial version
diff --git a/docs/quick_tutorial/scaffolds/MANIFEST.in b/docs/quick_tutorial/scaffolds/MANIFEST.in
new file mode 100644
index 000000000..91d3f763b
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/MANIFEST.in
@@ -0,0 +1,2 @@
+include *.txt *.ini *.cfg *.rst
+recursive-include scaffolds *.ico *.png *.css *.gif *.jpg *.pt *.txt *.mak *.mako *.js *.html *.xml
diff --git a/docs/quick_tutorial/scaffolds/README.txt b/docs/quick_tutorial/scaffolds/README.txt
new file mode 100644
index 000000000..7776dd2d5
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/README.txt
@@ -0,0 +1 @@
+scaffolds README
diff --git a/docs/quick_tutorial/scaffolds/development.ini b/docs/quick_tutorial/scaffolds/development.ini
new file mode 100644
index 000000000..b31d06194
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/development.ini
@@ -0,0 +1,60 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
+[app:main]
+use = egg:scaffolds
+
+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
+
+# 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, scaffolds
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_scaffolds]
+level = DEBUG
+handlers =
+qualname = scaffolds
+
+[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
diff --git a/docs/quick_tutorial/scaffolds/production.ini b/docs/quick_tutorial/scaffolds/production.ini
new file mode 100644
index 000000000..1418e6bf6
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/production.ini
@@ -0,0 +1,54 @@
+###
+# app configuration
+# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
+[app:main]
+use = egg:scaffolds
+
+pyramid.reload_templates = false
+pyramid.debug_authorization = false
+pyramid.debug_notfound = false
+pyramid.debug_routematch = false
+pyramid.default_locale_name = en
+
+###
+# 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, scaffolds
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+
+[logger_scaffolds]
+level = WARN
+handlers =
+qualname = scaffolds
+
+[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
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/__init__.py b/docs/quick_tutorial/scaffolds/scaffolds/__init__.py
new file mode 100644
index 000000000..ad5ecbc6f
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/__init__.py
@@ -0,0 +1,12 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ """ This function returns a Pyramid WSGI application.
+ """
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_static_view('static', 'static', cache_max_age=3600)
+ config.add_route('home', '/')
+ config.scan()
+ return config.make_wsgi_app()
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/favicon.ico b/docs/quick_tutorial/scaffolds/scaffolds/static/favicon.ico
new file mode 100644
index 000000000..71f837c9e
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/favicon.ico
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.png b/docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.png
new file mode 100644
index 000000000..1fbc873da
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/footerbg.png
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.png b/docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.png
new file mode 100644
index 000000000..0596f2020
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/headerbg.png
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css b/docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css
new file mode 100644
index 000000000..b7c8493d8
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/ie6.css
@@ -0,0 +1,8 @@
+* html img,
+* html .png{position:relative;behavior:expression((this.runtimeStyle.behavior="none")&&(this.pngSet?this.pngSet=true:(this.nodeName == "IMG" && this.src.toLowerCase().indexOf('.png')>-1?(this.runtimeStyle.backgroundImage = "none",
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.src + "',sizingMethod='image')",
+this.src = "static/transparent.gif"):(this.origBg = this.origBg? this.origBg :this.currentStyle.backgroundImage.toString().replace('url("','').replace('")',''),
+this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + this.origBg + "',sizingMethod='crop')",
+this.runtimeStyle.backgroundImage = "none")),this.pngSet=true)
+);}
+#wrap{display:table;height:100%}
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.png b/docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.png
new file mode 100644
index 000000000..2369cfb7d
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/middlebg.png
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css b/docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css
new file mode 100644
index 000000000..4b1c017cd
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/pylons.css
@@ -0,0 +1,372 @@
+html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td
+{
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-size: 100%; /* 16px */
+ vertical-align: baseline;
+ background: transparent;
+}
+
+body
+{
+ line-height: 1;
+}
+
+ol, ul
+{
+ list-style: none;
+}
+
+blockquote, q
+{
+ quotes: none;
+}
+
+blockquote:before, blockquote:after, q:before, q:after
+{
+ content: '';
+ content: none;
+}
+
+:focus
+{
+ outline: 0;
+}
+
+ins
+{
+ text-decoration: none;
+}
+
+del
+{
+ text-decoration: line-through;
+}
+
+table
+{
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+sub
+{
+ vertical-align: sub;
+ font-size: smaller;
+ line-height: normal;
+}
+
+sup
+{
+ vertical-align: super;
+ font-size: smaller;
+ line-height: normal;
+}
+
+ul, menu, dir
+{
+ display: block;
+ list-style-type: disc;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+ol
+{
+ display: block;
+ list-style-type: decimal-leading-zero;
+ margin: 1em 0;
+ padding-left: 40px;
+}
+
+li
+{
+ display: list-item;
+}
+
+ul ul, ul ol, ul dir, ul menu, ul dl, ol ul, ol ol, ol dir, ol menu, ol dl, dir ul, dir ol, dir dir, dir menu, dir dl, menu ul, menu ol, menu dir, menu menu, menu dl, dl ul, dl ol, dl dir, dl menu, dl dl
+{
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+ol ul, ul ul, menu ul, dir ul, ol menu, ul menu, menu menu, dir menu, ol dir, ul dir, menu dir, dir dir
+{
+ list-style-type: circle;
+}
+
+ol ol ul, ol ul ul, ol menu ul, ol dir ul, ol ol menu, ol ul menu, ol menu menu, ol dir menu, ol ol dir, ol ul dir, ol menu dir, ol dir dir, ul ol ul, ul ul ul, ul menu ul, ul dir ul, ul ol menu, ul ul menu, ul menu menu, ul dir menu, ul ol dir, ul ul dir, ul menu dir, ul dir dir, menu ol ul, menu ul ul, menu menu ul, menu dir ul, menu ol menu, menu ul menu, menu menu menu, menu dir menu, menu ol dir, menu ul dir, menu menu dir, menu dir dir, dir ol ul, dir ul ul, dir menu ul, dir dir ul, dir ol menu, dir ul menu, dir menu menu, dir dir menu, dir ol dir, dir ul dir, dir menu dir, dir dir dir
+{
+ list-style-type: square;
+}
+
+.hidden
+{
+ display: none;
+}
+
+p
+{
+ line-height: 1.5em;
+}
+
+h1
+{
+ font-size: 1.75em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h2
+{
+ font-size: 1.5em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h3
+{
+ font-size: 1.25em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+h4
+{
+ font-size: 1em;
+ line-height: 1.7em;
+ font-family: helvetica, verdana;
+}
+
+html, body
+{
+ width: 100%;
+ height: 100%;
+}
+
+body
+{
+ margin: 0;
+ padding: 0;
+ background-color: #fff;
+ position: relative;
+ font: 16px/24px NobileRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+}
+
+a
+{
+ color: #1b61d6;
+ text-decoration: none;
+}
+
+a:hover
+{
+ color: #e88f00;
+ text-decoration: underline;
+}
+
+body h1, body h2, body h3, body h4, body h5, body h6
+{
+ font-family: NeutonRegular, "Lucida Grande", Lucida, Verdana, sans-serif;
+ font-weight: 400;
+ color: #373839;
+ font-style: normal;
+}
+
+#wrap
+{
+ min-height: 100%;
+}
+
+#header, #footer
+{
+ width: 100%;
+ color: #fff;
+ height: 40px;
+ position: absolute;
+ text-align: center;
+ line-height: 40px;
+ overflow: hidden;
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+#header
+{
+ background: #000;
+ top: 0;
+ font-size: 14px;
+}
+
+#footer
+{
+ bottom: 0;
+ background: #000 url(footerbg.png) repeat-x 0 top;
+ position: relative;
+ margin-top: -40px;
+ clear: both;
+}
+
+.header, .footer
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.wrapper
+{
+ width: 100%;
+}
+
+#top, #top-small, #bottom
+{
+ width: 100%;
+}
+
+#top
+{
+ color: #000;
+ height: 230px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#top-small
+{
+ color: #000;
+ height: 60px;
+ background: #fff url(headerbg.png) repeat-x 0 top;
+ position: relative;
+}
+
+#bottom
+{
+ color: #222;
+ background-color: #fff;
+}
+
+.top, .top-small, .middle, .bottom
+{
+ width: 750px;
+ margin-right: auto;
+ margin-left: auto;
+}
+
+.top
+{
+ padding-top: 40px;
+}
+
+.top-small
+{
+ padding-top: 10px;
+}
+
+#middle
+{
+ width: 100%;
+ height: 100px;
+ background: url(middlebg.png) repeat-x;
+ border-top: 2px solid #fff;
+ border-bottom: 2px solid #b2b2b2;
+}
+
+.app-welcome
+{
+ margin-top: 25px;
+}
+
+.app-name
+{
+ color: #000;
+ font-weight: 700;
+}
+
+.bottom
+{
+ padding-top: 50px;
+}
+
+#left
+{
+ width: 350px;
+ float: left;
+ padding-right: 25px;
+}
+
+#right
+{
+ width: 350px;
+ float: right;
+ padding-left: 25px;
+}
+
+.align-left
+{
+ text-align: left;
+}
+
+.align-right
+{
+ text-align: right;
+}
+
+.align-center
+{
+ text-align: center;
+}
+
+ul.links
+{
+ margin: 0;
+ padding: 0;
+}
+
+ul.links li
+{
+ list-style-type: none;
+ font-size: 14px;
+}
+
+form
+{
+ border-style: none;
+}
+
+fieldset
+{
+ border-style: none;
+}
+
+input
+{
+ color: #222;
+ border: 1px solid #ccc;
+ font-family: sans-serif;
+ font-size: 12px;
+ line-height: 16px;
+}
+
+input[type=text], input[type=password]
+{
+ width: 205px;
+}
+
+input[type=submit]
+{
+ background-color: #ddd;
+ font-weight: 700;
+}
+
+/*Opera Fix*/
+body:before
+{
+ content: "";
+ height: 100%;
+ float: left;
+ width: 0;
+ margin-top: -32767px;
+}
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.png b/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.png
new file mode 100644
index 000000000..a5bc0ade7
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid-small.png
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.png b/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.png
new file mode 100644
index 000000000..347e05549
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/pyramid.png
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gif b/docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gif
new file mode 100644
index 000000000..0341802e5
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/static/transparent.gif
Binary files differ
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt b/docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt
new file mode 100644
index 000000000..b43a174e3
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/templates/mytemplate.pt
@@ -0,0 +1,73 @@
+<!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>The Pyramid Web Framework</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('scaffolds:static/favicon.ico')}" />
+ <link rel="stylesheet" href="${request.static_url('scaffolds:static/pylons.css')}" type="text/css" media="screen" charset="utf-8" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/nobile/stylesheet.css" media="screen" />
+ <link rel="stylesheet" href="http://static.pylonsproject.org/fonts/neuton/stylesheet.css" media="screen" />
+ <!--[if lte IE 6]>
+ <link rel="stylesheet" href="${request.static_url('scaffolds:static/ie6.css')}" type="text/css" media="screen" charset="utf-8" />
+ <![endif]-->
+</head>
+<body>
+ <div id="wrap">
+ <div id="top">
+ <div class="top align-center">
+ <div><img src="${request.static_url('scaffolds:static/pyramid.png')}" width="750" height="169" alt="pyramid"/></div>
+ </div>
+ </div>
+ <div id="middle">
+ <div class="middle align-center">
+ <p class="app-welcome">
+ Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
+ the Pyramid Web Framework.
+ </p>
+ </div>
+ </div>
+ <div id="bottom">
+ <div class="bottom">
+ <div id="left" class="align-right">
+ <h2>Search documentation</h2>
+ <form method="get" action="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/search.html">
+ <input type="text" id="q" name="q" value="" />
+ <input type="submit" id="x" value="Go" />
+ </form>
+ </div>
+ <div id="right" class="align-left">
+ <h2>Pyramid links</h2>
+ <ul class="links">
+ <li>
+ <a href="http://pylonsproject.org">Pylons Website</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#narrative-documentation">Narrative Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#reference-material">API Documentation</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#tutorials">Tutorials</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#detailed-change-history">Change History</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#sample-applications">Sample Applications</a>
+ </li>
+ <li>
+ <a href="http://docs.pylonsproject.org/projects/pyramid/en/1.4-branch/#support-and-development">Support and Development</a>
+ </li>
+ <li>
+ <a href="irc://irc.freenode.net#pyramid">IRC Channel</a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+ </div>
+</body>
+</html>
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/tests.py b/docs/quick_tutorial/scaffolds/scaffolds/tests.py
new file mode 100644
index 000000000..4f906ffa9
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/tests.py
@@ -0,0 +1,17 @@
+import unittest
+
+from pyramid import testing
+
+
+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'], 'scaffolds')
diff --git a/docs/quick_tutorial/scaffolds/scaffolds/views.py b/docs/quick_tutorial/scaffolds/scaffolds/views.py
new file mode 100644
index 000000000..db90d8364
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/scaffolds/views.py
@@ -0,0 +1,6 @@
+from pyramid.view import view_config
+
+
+@view_config(route_name='home', renderer='templates/mytemplate.pt')
+def my_view(request):
+ return {'project': 'scaffolds'}
diff --git a/docs/quick_tutorial/scaffolds/setup.cfg b/docs/quick_tutorial/scaffolds/setup.cfg
new file mode 100644
index 000000000..c980261e3
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/setup.cfg
@@ -0,0 +1,27 @@
+[nosetests]
+match = ^test
+nocapture = 1
+cover-package = scaffolds
+with-coverage = 1
+cover-erase = 1
+
+[compile_catalog]
+directory = scaffolds/locale
+domain = scaffolds
+statistics = true
+
+[extract_messages]
+add_comments = TRANSLATORS:
+output_file = scaffolds/locale/scaffolds.pot
+width = 80
+
+[init_catalog]
+domain = scaffolds
+input_file = scaffolds/locale/scaffolds.pot
+output_dir = scaffolds/locale
+
+[update_catalog]
+domain = scaffolds
+input_file = scaffolds/locale/scaffolds.pot
+output_dir = scaffolds/locale
+previous = true
diff --git a/docs/quick_tutorial/scaffolds/setup.py b/docs/quick_tutorial/scaffolds/setup.py
new file mode 100644
index 000000000..ec95946a5
--- /dev/null
+++ b/docs/quick_tutorial/scaffolds/setup.py
@@ -0,0 +1,42 @@
+import os
+
+from setuptools import setup, find_packages
+
+here = os.path.abspath(os.path.dirname(__file__))
+with open(os.path.join(here, 'README.txt')) as f:
+ README = f.read()
+with open(os.path.join(here, 'CHANGES.txt')) as f:
+ CHANGES = f.read()
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'waitress',
+ ]
+
+setup(name='scaffolds',
+ version='0.0',
+ description='scaffolds',
+ 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="scaffolds",
+ entry_points="""\
+ [paste.app_factory]
+ main = scaffolds:main
+ """,
+ )
diff --git a/docs/quick_tutorial/sessions.rst b/docs/quick_tutorial/sessions.rst
new file mode 100644
index 000000000..ba26d0133
--- /dev/null
+++ b/docs/quick_tutorial/sessions.rst
@@ -0,0 +1,100 @@
+.. _qtut_sessions:
+
+=================================
+17: Transient Data Using Sessions
+=================================
+
+Store and retrieve non-permanent data in Pyramid sessions.
+
+Background
+==========
+
+When people use your web application, they frequently perform a task
+that requires semi-permanent data to be saved. For example, a shopping
+cart. This is called a :term:`session`.
+
+Pyramid has basic built-in support for sessions, with add-ons such as
+*dogpile.cache* (or your own custom sessioning engine) that provide
+richer session support. Let's take a look at the
+:ref:`built-in sessioning support <sessions_chapter>`.
+
+Objectives
+==========
+
+- Make a session factory using a built-in, simple Pyramid sessioning
+ system
+
+- Change our code to use a session
+
+Steps
+=====
+
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes sessions; cd sessions
+ $ $VENV/bin/python setup.py develop
+
+#. Our ``sessions/tutorial/__init__.py`` needs a choice of session
+ factory to get registered with the :term:`configurator`:
+
+ .. literalinclude:: sessions/tutorial/__init__.py
+ :linenos:
+
+#. Our views in ``sessions/tutorial/views.py`` can now use
+ ``request.session``:
+
+ .. literalinclude:: sessions/tutorial/views.py
+ :linenos:
+
+#. The template at ``sessions/tutorial/home.pt`` can display the value:
+
+ .. literalinclude:: sessions/tutorial/home.pt
+ :language: html
+ :linenos:
+
+#. Make sure the tests still pass:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ and http://localhost:6543/howdy
+ in your browser. As you reload and switch between those URLs, note
+ that the counter increases and is *not* specific to the URL.
+
+#. Restart the application and revisit the page. Note that counter
+ still increases from where it left off.
+
+Analysis
+========
+
+Pyramid's :term:`request` object now has a ``session`` attribute
+that we can use in our view code. It acts like a dictionary.
+
+Since all the views are using the same counter, we made the counter a
+Python property at the view class level. With this, each reload will
+increase the counter displayed in our template.
+
+In web development, "flash messages" are notes for the user that need
+to appear on a screen after a future web request. For example,
+when you add an item using a form ``POST``, the site usually issues a
+second HTTP Redirect web request to view the new item. You might want a
+message to appear after that second web request saying "Your item was
+added." You can't just return it in the web response for the POST,
+as it will be tossed out during the second web requests.
+
+Flash messages are a technique where messages can be stored between
+requests, using sessions, then removed when they finally get displayed.
+
+.. seealso::
+ :ref:`sessions_chapter`,
+ :ref:`flash_messages`, and
+ :ref:`session_module`.
diff --git a/docs/quick_tutorial/sessions/development.ini b/docs/quick_tutorial/sessions/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/sessions/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/sessions/setup.py b/docs/quick_tutorial/sessions/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/sessions/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/sessions/tutorial/__init__.py b/docs/quick_tutorial/sessions/tutorial/__init__.py
new file mode 100644
index 000000000..ecf57bb32
--- /dev/null
+++ b/docs/quick_tutorial/sessions/tutorial/__init__.py
@@ -0,0 +1,14 @@
+from pyramid.config import Configurator
+from pyramid.session import UnencryptedCookieSessionFactoryConfig
+
+
+def main(global_config, **settings):
+ my_session_factory = UnencryptedCookieSessionFactoryConfig(
+ 'itsaseekreet')
+ config = Configurator(settings=settings,
+ session_factory=my_session_factory)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/sessions/tutorial/home.pt b/docs/quick_tutorial/sessions/tutorial/home.pt
new file mode 100644
index 000000000..0b27ba1d8
--- /dev/null
+++ b/docs/quick_tutorial/sessions/tutorial/home.pt
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>Hi ${name}</h1>
+<p>Count: ${view.counter}</p>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/sessions/tutorial/tests.py b/docs/quick_tutorial/sessions/tutorial/tests.py
new file mode 100644
index 000000000..4381235ec
--- /dev/null
+++ b/docs/quick_tutorial/sessions/tutorial/tests.py
@@ -0,0 +1,44 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual('Home View', response['name'])
+
+ def test_hello(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.hello()
+ self.assertEqual('Hello View', response['name'])
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hi Home View', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<h1>Hi Hello View', res.body)
diff --git a/docs/quick_tutorial/sessions/tutorial/views.py b/docs/quick_tutorial/sessions/tutorial/views.py
new file mode 100644
index 000000000..a4659d265
--- /dev/null
+++ b/docs/quick_tutorial/sessions/tutorial/views.py
@@ -0,0 +1,29 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @property
+ def counter(self):
+ session = self.request.session
+ if 'counter' in session:
+ session['counter'] += 1
+ else:
+ session['counter'] = 1
+
+ return session['counter']
+
+
+ @view_config(route_name='home')
+ def home(self):
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello')
+ def hello(self):
+ return {'name': 'Hello View'}
diff --git a/docs/quick_tutorial/static_assets.rst b/docs/quick_tutorial/static_assets.rst
new file mode 100644
index 000000000..19d33f00f
--- /dev/null
+++ b/docs/quick_tutorial/static_assets.rst
@@ -0,0 +1,91 @@
+.. _qtut_static_assets:
+
+==========================================
+13: CSS/JS/Images Files With Static Assets
+==========================================
+
+Of course the Web is more than just markup. You need static assets:
+CSS, JS, and images. Let's point our web app at a directory where
+Pyramid will serve some static assets.
+
+Objectives
+==========
+
+- Publish a directory of static assets at a URL
+
+- Use Pyramid to help generate URLs to files in that directory
+
+Steps
+=====
+
+#. First we copy the results of the ``view_classes`` step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r view_classes static_assets; cd static_assets
+ $ $VENV/bin/python setup.py develop
+
+#. We add a call ``config.add_static_view in
+ ``static_assets/tutorial/__init__.py``:
+
+ .. literalinclude:: static_assets/tutorial/__init__.py
+ :linenos:
+
+#. We can add a CSS link in the ``<head>`` of our template at
+ ``static_assets/tutorial/home.pt``:
+
+ .. literalinclude:: static_assets/tutorial/home.pt
+ :language: html
+
+#. Add a CSS file at
+ ``static_assets/tutorial/static/app.css``:
+
+ .. literalinclude:: static_assets/tutorial/static/app.css
+ :language: css
+
+#. Make sure we haven't broken any existing code by running the tests:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/nosetests tutorial
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ in your browser and note the new font.
+
+Analysis
+========
+
+We changed our WSGI application to map requests under
+http://localhost:6543/static/ to files and directories inside a
+``static`` directory inside our ``tutorial`` package. This directory
+contained ``app.css``.
+
+We linked to the CSS in our template. We could have hard-coded this
+link to ``/static/app.css``. But what if the site is later moved under
+``/somesite/static/``? Or perhaps the web developer changes the
+arrangement on disk? Pyramid gives a helper that provides flexibility
+on URL generation:
+
+.. code-block:: html
+
+ ${request.static_url('tutorial:static/app.css')}
+
+This matches the ``path='tutorial:static'`` in our
+``config.add_static_view`` registration. By using ``request.static_url``
+to generate the full URL to the static assets, you both ensure you stay
+in sync with the configuration and gain refactoring flexibility later.
+
+Extra Credit
+============
+
+#. There is also a ``request.static_path`` API. How does this differ from
+ ``request.static_url``?
+
+.. seealso:: :ref:`assets_chapter`,
+ :ref:`preventing_http_caching`, and
+ :ref:`influencing_http_caching`
diff --git a/docs/quick_tutorial/static_assets/development.ini b/docs/quick_tutorial/static_assets/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/static_assets/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/static_assets/setup.py b/docs/quick_tutorial/static_assets/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/static_assets/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/static_assets/tutorial/__init__.py b/docs/quick_tutorial/static_assets/tutorial/__init__.py
new file mode 100644
index 000000000..e244c2997
--- /dev/null
+++ b/docs/quick_tutorial/static_assets/tutorial/__init__.py
@@ -0,0 +1,11 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.add_static_view(name='static', path='tutorial:static')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/static_assets/tutorial/home.pt b/docs/quick_tutorial/static_assets/tutorial/home.pt
new file mode 100644
index 000000000..5d347f057
--- /dev/null
+++ b/docs/quick_tutorial/static_assets/tutorial/home.pt
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+ <link rel="stylesheet"
+ href="${request.static_url('tutorial:static/app.css') }"/>
+</head>
+<body>
+<h1>Hi ${name}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/static_assets/tutorial/static/app.css b/docs/quick_tutorial/static_assets/tutorial/static/app.css
new file mode 100644
index 000000000..f8acf3164
--- /dev/null
+++ b/docs/quick_tutorial/static_assets/tutorial/static/app.css
@@ -0,0 +1,4 @@
+body {
+ margin: 2em;
+ font-family: sans-serif;
+} \ No newline at end of file
diff --git a/docs/quick_tutorial/static_assets/tutorial/tests.py b/docs/quick_tutorial/static_assets/tutorial/tests.py
new file mode 100644
index 000000000..4381235ec
--- /dev/null
+++ b/docs/quick_tutorial/static_assets/tutorial/tests.py
@@ -0,0 +1,44 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual('Home View', response['name'])
+
+ def test_hello(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.hello()
+ self.assertEqual('Hello View', response['name'])
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hi Home View', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<h1>Hi Hello View', res.body)
diff --git a/docs/quick_tutorial/static_assets/tutorial/views.py b/docs/quick_tutorial/static_assets/tutorial/views.py
new file mode 100644
index 000000000..a56c0adbf
--- /dev/null
+++ b/docs/quick_tutorial/static_assets/tutorial/views.py
@@ -0,0 +1,18 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(route_name='home')
+ def home(self):
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello')
+ def hello(self):
+ return {'name': 'Hello View'}
diff --git a/docs/quick_tutorial/templating.rst b/docs/quick_tutorial/templating.rst
new file mode 100644
index 000000000..d73067f48
--- /dev/null
+++ b/docs/quick_tutorial/templating.rst
@@ -0,0 +1,123 @@
+.. _qtut_templating:
+
+===================================
+08: HTML Generation With Templating
+===================================
+
+Most web frameworks don't embed HTML in programming code. Instead,
+they pass data into a templating system. In this step we look at the
+basics of using HTML templates in Pyramid.
+
+Background
+==========
+
+Ouch. We have been making our own ``Response`` and filling the response
+body with HTML. You usually won't embed an HTML string directly in
+Python, but instead, will use a templating language.
+
+Pyramid doesn't mandate a particular database system, form library,
+etc. It encourages replaceability. This applies equally to templating,
+which is fortunate: developers have strong views about template
+languages. As of Pyramid 1.5a2, Pyramid doesn't even bundle a template
+language!
+
+It does, however, have strong ties to Jinja2, Mako, and Chameleon. In
+this step we see how to add ``pyramid_chameleon`` to your project,
+then change your views to use templating.
+
+Objectives
+==========
+
+- Enable the ``pyramid_chameleon`` Pyramid add-on
+
+- Generate HTML from template files
+
+- Connect the templates as "renderers" for view code
+
+- Change the view code to simply return data
+
+Steps
+=====
+
+#. Let's begin by using the previous package as a starting point for a
+ new project:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r views templating; cd templating
+
+#. This step depends on ``pyramid_chameleon``, so add it as a dependency
+ in ``templating/setup.py``:
+
+ .. literalinclude:: templating/setup.py
+ :linenos:
+
+#. Now we can activate the development-mode distribution:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/python setup.py develop
+
+#. We need to connect ``pyramid_chameleon`` as a renderer by making a
+ call in the setup of ``templating/tutorial/__init__.py``:
+
+ .. literalinclude:: templating/tutorial/__init__.py
+ :linenos:
+
+#. Our ``templating/tutorial/views.py`` no longer has HTML in it:
+
+ .. literalinclude:: templating/tutorial/views.py
+ :linenos:
+
+#. Instead we have ``templating/tutorial/home.pt`` as a template:
+
+ .. literalinclude:: templating/tutorial/home.pt
+ :language: html
+
+#. For convenience, change ``templating/development.ini`` to reload
+ templates automatically with ``pyramid.reload_templates``:
+
+ .. literalinclude:: templating/development.ini
+ :language: ini
+
+#. Our unit tests in ``templating/tutorial/tests.py`` can focus on
+ data:
+
+ .. literalinclude:: templating/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+
+ $ $VENV/bin/nosetests tutorial
+ .
+ ----------------------------------------------------------------------
+ Ran 4 tests in 0.141s
+
+ OK
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ and http://localhost:6543/howdy
+ in your browser.
+
+Analysis
+========
+
+Ahh, that looks better. We have a view that is focused on Python code.
+Our ``@view_config`` decorator specifies a :term:`renderer` that points
+our template file. Our view then simply returns data which is then
+supplied to our template. Note that we used the same template for both
+views.
+
+Note the effect on testing. We can focus on having a data-oriented
+contract with our view code.
+
+.. seealso:: :ref:`templates_chapter`, :ref:`debugging_templates`, and
+ :ref:`available_template_system_bindings`.
diff --git a/docs/quick_tutorial/templating/development.ini b/docs/quick_tutorial/templating/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/templating/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/templating/setup.py b/docs/quick_tutorial/templating/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/templating/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/templating/tutorial/__init__.py b/docs/quick_tutorial/templating/tutorial/__init__.py
new file mode 100644
index 000000000..c3e1c9eef
--- /dev/null
+++ b/docs/quick_tutorial/templating/tutorial/__init__.py
@@ -0,0 +1,10 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/templating/tutorial/home.pt b/docs/quick_tutorial/templating/tutorial/home.pt
new file mode 100644
index 000000000..a0cc08e7a
--- /dev/null
+++ b/docs/quick_tutorial/templating/tutorial/home.pt
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>Hi ${name}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/templating/tutorial/tests.py b/docs/quick_tutorial/templating/tutorial/tests.py
new file mode 100644
index 000000000..d06a62982
--- /dev/null
+++ b/docs/quick_tutorial/templating/tutorial/tests.py
@@ -0,0 +1,44 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import home
+
+ request = testing.DummyRequest()
+ response = home(request)
+ # Our view now returns data
+ self.assertEqual('Home View', response['name'])
+
+ def test_hello(self):
+ from .views import hello
+
+ request = testing.DummyRequest()
+ response = hello(request)
+ # Our view now returns data
+ self.assertEqual('Hello View', response['name'])
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hi Home View', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<h1>Hi Hello View', res.body)
diff --git a/docs/quick_tutorial/templating/tutorial/views.py b/docs/quick_tutorial/templating/tutorial/views.py
new file mode 100644
index 000000000..979d69c43
--- /dev/null
+++ b/docs/quick_tutorial/templating/tutorial/views.py
@@ -0,0 +1,13 @@
+from pyramid.view import view_config
+
+
+# First view, available at http://localhost:6543/
+@view_config(route_name='home', renderer='home.pt')
+def home(request):
+ return {'name': 'Home View'}
+
+
+# /howdy
+@view_config(route_name='hello', renderer='home.pt')
+def hello(request):
+ return {'name': 'Hello View'}
diff --git a/docs/quick_tutorial/tutorial_approach.rst b/docs/quick_tutorial/tutorial_approach.rst
new file mode 100644
index 000000000..52d768306
--- /dev/null
+++ b/docs/quick_tutorial/tutorial_approach.rst
@@ -0,0 +1,45 @@
+=================
+Tutorial Approach
+=================
+
+This tutorial uses conventions to keep the introduction focused and
+concise. Details, references, and deeper discussions are mentioned in
+"See Also" notes.
+
+.. seealso:: This is an example "See Also" note.
+
+This "Getting Started" tutorial is broken into independent steps,
+starting with the smallest possible "single file WSGI app" example.
+Each of these steps introduce a topic and a very small set of concepts
+via working code. The steps each correspond to a directory in this
+repo, where each step/topic/directory is a Python package.
+
+To successfully run each step::
+
+ $ cd request_response
+ $ $VENV/bin/python setup.py develop
+
+...and repeat for each step you would like to work on. In most cases we
+will start with the results of an earlier step.
+
+Directory Tree
+==============
+
+As we develop our tutorial our directory tree will resemble the
+structure below::
+
+ quicktutorial/
+ request_response/
+ development.ini
+ setup.py
+ tutorial/
+ __init__.py
+ home.pt
+ tests.py
+ views.py
+
+Each of the first-level directories (e.g. ``request_response``) is a
+*Python project* (except, as noted, the ``hello_world`` step.) The
+``tutorial`` directory is a *Python package*. At the end of each step,
+we copy a previous directory into a new directory to use as a starting
+point. \ No newline at end of file
diff --git a/docs/quick_tutorial/unit_testing.rst b/docs/quick_tutorial/unit_testing.rst
new file mode 100644
index 000000000..73b33c588
--- /dev/null
+++ b/docs/quick_tutorial/unit_testing.rst
@@ -0,0 +1,119 @@
+.. _qtut_unit_testing:
+
+===========================
+05: Unit Tests and ``nose``
+===========================
+
+Provide unit testing for our project's Python code.
+
+Background
+==========
+
+As the mantra says, "Untested code is broken code." The Python
+community has had a long culture of writing test scripts which ensure
+that your code works correctly as you write it and maintain it in the
+future. Pyramid has always had a deep commitment to testing,
+with 100% test coverage from the earliest pre-releases.
+
+Python includes a
+:ref:`unit testing framework <python:unittest-minimal-example>` in its
+standard library. Over the years a number of Python projects, such as
+`nose <https://pypi.python.org/pypi/nose/>`_, have extended this
+framework with alternative test runners that provide more convenience
+and functionality. The Pyramid developers use ``nose``, which we'll thus
+use in this tutorial.
+
+Don't worry, this tutorial won't be pedantic about "test-driven
+development" (TDD.) We'll do just enough to ensure that, in each step,
+we haven't majorly broken the code. As you're writing your code you
+might find this more convenient than changing to your browser
+constantly and clicking reload.
+
+We'll also leave discussion of
+`coverage <https://pypi.python.org/pypi/coverage>`_ for another section.
+
+Objectives
+==========
+
+- Write unit tests that ensure the quality of our code
+
+- Install a Python package (``nose``) which helps in our testing
+
+Steps
+=====
+
+#. First we copy the results of the previous step, as well as install
+ the ``nose`` package:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r debugtoolbar unit_testing; cd unit_testing
+ $ $VENV/bin/python setup.py develop
+ $ $VENV/bin/easy_install nose
+
+#. Now we write a simple unit test in ``unit_testing/tutorial/tests.py``:
+
+ .. literalinclude:: unit_testing/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+
+ $ $VENV/bin/nosetests tutorial
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.141s
+
+ OK
+
+Analysis
+========
+
+Our ``tests.py`` imports the Python standard unit testing framework. To
+make writing Pyramid-oriented tests more convenient, Pyramid supplies
+some ``pyramid.testing`` helpers which we use in the test setup and
+teardown. Our one test imports the view, makes a dummy request, and sees
+if the view returns what we expected.
+
+The ``tests.HelloWorldViewTests.test_hello_world`` test is a small
+example of a unit test. First, we import the view inside each test. Why
+not import at the top, like in normal Python code? Because imports can
+cause effects that break a test. We'd like our tests to be in *units*,
+hence the name *unit* testing. Each test should isolate itself to the
+correct degree.
+
+Our test then makes a fake incoming web request, then calls our Pyramid
+view. We test the HTTP status code on the response to make sure it
+matches our expectations.
+
+Note that our use of ``pyramid.testing.setUp()`` and
+``pyramid.testing.tearDown()`` aren't actually necessary here; they are only
+necessary when your test needs to make use of the ``config`` object (it's a
+Configurator) to add stuff to the configuration state before calling the view.
+
+Extra Credit
+============
+
+#. Change the test to assert that the response status code should be
+ ``404`` (meaning, not found.) Run ``nosetests`` again. Read the
+ error report and see if you can decipher what it is telling you.
+
+#. As a more realistic example, put the ``tests.py`` back as you found
+ it and put an error in your view, such as a reference to a
+ non-existing variable. Run the tests and see how this is more
+ convenient than reloading your browser and going back to your code.
+
+#. Finally, for the most realistic test, read about Pyramid ``Response``
+ objects and see how to change the response code. Run the tests and
+ see how testing confirms the "contract" that your code claims to
+ support.
+
+#. How could we add a unit test assertion to test the HTML value of the
+ response body?
+
+#. Why do we import the ``hello_world`` view function *inside* the
+ ``test_hello_world`` method instead of at the top of the module?
+
+.. seealso:: See Also: :ref:`testing_chapter`
diff --git a/docs/quick_tutorial/unit_testing/development.ini b/docs/quick_tutorial/unit_testing/development.ini
new file mode 100644
index 000000000..470d92c57
--- /dev/null
+++ b/docs/quick_tutorial/unit_testing/development.ini
@@ -0,0 +1,40 @@
+[app:main]
+use = egg:tutorial
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/unit_testing/setup.py b/docs/quick_tutorial/unit_testing/setup.py
new file mode 100644
index 000000000..9997984d3
--- /dev/null
+++ b/docs/quick_tutorial/unit_testing/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/unit_testing/tutorial/__init__.py b/docs/quick_tutorial/unit_testing/tutorial/__init__.py
new file mode 100644
index 000000000..2b4e84f30
--- /dev/null
+++ b/docs/quick_tutorial/unit_testing/tutorial/__init__.py
@@ -0,0 +1,13 @@
+from pyramid.config import Configurator
+from pyramid.response import Response
+
+
+def hello_world(request):
+ return Response('<body><h1>Hello World!</h1></body>')
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.add_route('hello', '/')
+ config.add_view(hello_world, route_name='hello')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/unit_testing/tutorial/tests.py b/docs/quick_tutorial/unit_testing/tutorial/tests.py
new file mode 100644
index 000000000..66029b421
--- /dev/null
+++ b/docs/quick_tutorial/unit_testing/tutorial/tests.py
@@ -0,0 +1,18 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_hello_world(self):
+ from tutorial import hello_world
+
+ request = testing.DummyRequest()
+ response = hello_world(request)
+ self.assertEqual(response.status_code, 200)
diff --git a/docs/quick_tutorial/view_classes.rst b/docs/quick_tutorial/view_classes.rst
new file mode 100644
index 000000000..58ab43e40
--- /dev/null
+++ b/docs/quick_tutorial/view_classes.rst
@@ -0,0 +1,98 @@
+.. _qtut_view_classes:
+
+======================================
+09: Organizing Views With View Classes
+======================================
+
+Change our view functions to be methods on a view class,
+then move some declarations to the class level.
+
+Background
+==========
+
+So far our views have been simple, free-standing functions. Many times
+your views are related: different ways to look at or work on the same
+data or a REST API that handles multiple operations. Grouping these
+together as a
+:ref:`view class <class_as_view>` makes sense:
+
+- Group views
+
+- Centralize some repetitive defaults
+
+- Share some state and helpers
+
+In this step we just do the absolute minimum to convert the existing
+views to a view class. In a later tutorial step we'll examine view
+classes in depth.
+
+Objectives
+==========
+
+- Group related views into a view class
+
+- Centralize configuration with class-level ``@view_defaults``
+
+Steps
+=====
+
+
+#. First we copy the results of the previous step:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r templating view_classes; cd view_classes
+ $ $VENV/bin/python setup.py develop
+
+#. Our ``view_classes/tutorial/views.py`` now has a view class with
+ our two views:
+
+ .. literalinclude:: view_classes/tutorial/views.py
+ :linenos:
+
+#. Our unit tests in ``view_classes/tutorial/tests.py`` don't run,
+ so let's modify the to import the view class and make an instance
+ before getting a response:
+
+ .. literalinclude:: view_classes/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+
+ $ $VENV/bin/nosetests tutorial
+ .
+ ----------------------------------------------------------------------
+ Ran 4 tests in 0.141s
+
+ OK
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ and http://localhost:6543/howdy
+ in your browser.
+
+Analysis
+========
+
+To ease the transition to view classes, we didn't introduce any new
+functionality. We simply changed the view functions to methods on a
+view class, then updated the tests.
+
+In our ``TutorialViews`` view class you can see that our two view
+classes are logically grouped together as methods on a common class.
+Since the two views shared the same template, we could move that to a
+``@view_defaults`` decorator on at the class level.
+
+The tests needed to change. Obviously we needed to import the view
+class. But you can also see the pattern in the tests of instantiating
+the view class with the dummy request first, then calling the view
+method being tested.
+
+.. seealso:: :ref:`class_as_view`
diff --git a/docs/quick_tutorial/view_classes/development.ini b/docs/quick_tutorial/view_classes/development.ini
new file mode 100644
index 000000000..62e0c5123
--- /dev/null
+++ b/docs/quick_tutorial/view_classes/development.ini
@@ -0,0 +1,41 @@
+[app:main]
+use = egg:tutorial
+pyramid.reload_templates = true
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/view_classes/setup.py b/docs/quick_tutorial/view_classes/setup.py
new file mode 100644
index 000000000..2221b72e9
--- /dev/null
+++ b/docs/quick_tutorial/view_classes/setup.py
@@ -0,0 +1,14 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+ 'pyramid_chameleon'
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/view_classes/tutorial/__init__.py b/docs/quick_tutorial/view_classes/tutorial/__init__.py
new file mode 100644
index 000000000..c3e1c9eef
--- /dev/null
+++ b/docs/quick_tutorial/view_classes/tutorial/__init__.py
@@ -0,0 +1,10 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/view_classes/tutorial/home.pt b/docs/quick_tutorial/view_classes/tutorial/home.pt
new file mode 100644
index 000000000..a0cc08e7a
--- /dev/null
+++ b/docs/quick_tutorial/view_classes/tutorial/home.pt
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <title>Quick Tour: ${name}</title>
+</head>
+<body>
+<h1>Hi ${name}</h1>
+</body>
+</html> \ No newline at end of file
diff --git a/docs/quick_tutorial/view_classes/tutorial/tests.py b/docs/quick_tutorial/view_classes/tutorial/tests.py
new file mode 100644
index 000000000..4381235ec
--- /dev/null
+++ b/docs/quick_tutorial/view_classes/tutorial/tests.py
@@ -0,0 +1,44 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.home()
+ self.assertEqual('Home View', response['name'])
+
+ def test_hello(self):
+ from .views import TutorialViews
+
+ request = testing.DummyRequest()
+ inst = TutorialViews(request)
+ response = inst.hello()
+ self.assertEqual('Hello View', response['name'])
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<h1>Hi Home View', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<h1>Hi Hello View', res.body)
diff --git a/docs/quick_tutorial/view_classes/tutorial/views.py b/docs/quick_tutorial/view_classes/tutorial/views.py
new file mode 100644
index 000000000..58db53c4a
--- /dev/null
+++ b/docs/quick_tutorial/view_classes/tutorial/views.py
@@ -0,0 +1,17 @@
+from pyramid.view import (
+ view_config,
+ view_defaults
+ )
+
+@view_defaults(renderer='home.pt')
+class TutorialViews:
+ def __init__(self, request):
+ self.request = request
+
+ @view_config(route_name='home')
+ def home(self):
+ return {'name': 'Home View'}
+
+ @view_config(route_name='hello')
+ def hello(self):
+ return {'name': 'Hello View'}
diff --git a/docs/quick_tutorial/views.rst b/docs/quick_tutorial/views.rst
new file mode 100644
index 000000000..15785e902
--- /dev/null
+++ b/docs/quick_tutorial/views.rst
@@ -0,0 +1,122 @@
+.. _qtut_views:
+
+=================================
+07: Basic Web Handling With Views
+=================================
+
+Organize a views module with decorators and multiple views.
+
+Background
+==========
+
+For the examples so far, the ``hello_world`` function is a "view". In
+Pyramid, views are the primary way to accept web requests and return
+responses.
+
+So far our examples place everything in one file:
+
+- The view function
+
+- Its registration with the configurator
+
+- The route to map it to a URL
+
+- The WSGI application launcher
+
+Let's move the views out to their own ``views.py`` module and change
+our startup code to scan that module, looking for decorators that setup
+the views. Let's also add a second view and update our tests.
+
+Objectives
+==========
+
+- Views in a module that is scanned by the configurator
+
+- Decorators that do declarative configuration
+
+Steps
+=====
+
+#. Let's begin by using the previous package as a starting point for a
+ new distribution, then making it active:
+
+ .. code-block:: bash
+
+ $ cd ..; cp -r function_testing views; cd views
+ $ $VENV/bin/python setup.py develop
+
+#. Our ``views/tutorial/__init__.py`` gets a lot shorter:
+
+ .. literalinclude:: views/tutorial/__init__.py
+ :linenos:
+
+#. Let's add a module ``views/tutorial/views.py`` that is focused on
+ handling requests and responses:
+
+ .. literalinclude:: views/tutorial/views.py
+ :linenos:
+
+#. Update the tests to cover the two new views:
+
+ .. literalinclude:: views/tutorial/tests.py
+ :linenos:
+
+#. Now run the tests:
+
+ .. code-block:: bash
+
+
+ $ $VENV/bin/nosetests tutorial
+ .
+ ----------------------------------------------------------------------
+ Ran 4 tests in 0.141s
+
+ OK
+
+#. Run your Pyramid application with:
+
+ .. code-block:: bash
+
+ $ $VENV/bin/pserve development.ini --reload
+
+#. Open http://localhost:6543/ and http://localhost:6543/howdy
+ in your browser.
+
+Analysis
+========
+
+We added some more URLs, but we also removed the view code from the
+application startup code in ``tutorial/__init__.py``.
+Our views, and their view registrations (via decorators) are now in a
+module ``views.py`` which is scanned via ``config.scan('.views')``.
+
+We have 2 views, each leading to the other. If you start at
+http://localhost:6543/, you get a response with a link to the next
+view. The ``hello_view`` (available at the URL ``/howdy``) has a link
+back to the first view.
+
+This step also shows that the name appearing in the URL,
+the name of the "route" that maps a URL to a view,
+and the name of the view, can all be different. More on routes later.
+
+Earlier we saw ``config.add_view`` as one way to configure a view. This
+section introduces ``@view_config``. Pyramid's configuration supports
+:term:`imperative configuration`, such as the
+``config.add_view`` in the previous example. You can also use
+:term:`declarative configuration`, in which a Python
+:term:`python:decorator`
+is placed on the line above the view. Both approaches result in the
+same final configuration, thus usually, it is simply a matter of taste.
+
+Extra Credit
+============
+
+#. What does the dot in ``.views`` signify?
+
+#. Why might ``assertIn`` be a better choice in testing the text in
+ responses than ``assertEqual``?
+
+.. seealso:: :ref:`views_chapter`,
+ :ref:`view_config_chapter`, and
+ :ref:`debugging_view_configuration`
+
diff --git a/docs/quick_tutorial/views/development.ini b/docs/quick_tutorial/views/development.ini
new file mode 100644
index 000000000..470d92c57
--- /dev/null
+++ b/docs/quick_tutorial/views/development.ini
@@ -0,0 +1,40 @@
+[app:main]
+use = egg:tutorial
+pyramid.includes =
+ pyramid_debugtoolbar
+
+[server:main]
+use = egg:pyramid#wsgiref
+host = 0.0.0.0
+port = 6543
+
+# Begin logging configuration
+
+[loggers]
+keys = root, tutorial
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[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
diff --git a/docs/quick_tutorial/views/setup.py b/docs/quick_tutorial/views/setup.py
new file mode 100644
index 000000000..9997984d3
--- /dev/null
+++ b/docs/quick_tutorial/views/setup.py
@@ -0,0 +1,13 @@
+from setuptools import setup
+
+requires = [
+ 'pyramid',
+]
+
+setup(name='tutorial',
+ install_requires=requires,
+ entry_points="""\
+ [paste.app_factory]
+ main = tutorial:main
+ """,
+) \ No newline at end of file
diff --git a/docs/quick_tutorial/views/tutorial/__init__.py b/docs/quick_tutorial/views/tutorial/__init__.py
new file mode 100644
index 000000000..013d4538f
--- /dev/null
+++ b/docs/quick_tutorial/views/tutorial/__init__.py
@@ -0,0 +1,9 @@
+from pyramid.config import Configurator
+
+
+def main(global_config, **settings):
+ config = Configurator(settings=settings)
+ config.add_route('home', '/')
+ config.add_route('hello', '/howdy')
+ config.scan('.views')
+ return config.make_wsgi_app() \ No newline at end of file
diff --git a/docs/quick_tutorial/views/tutorial/tests.py b/docs/quick_tutorial/views/tutorial/tests.py
new file mode 100644
index 000000000..f1757757c
--- /dev/null
+++ b/docs/quick_tutorial/views/tutorial/tests.py
@@ -0,0 +1,44 @@
+import unittest
+
+from pyramid import testing
+
+
+class TutorialViewTests(unittest.TestCase):
+ def setUp(self):
+ self.config = testing.setUp()
+
+ def tearDown(self):
+ testing.tearDown()
+
+ def test_home(self):
+ from .views import home
+
+ request = testing.DummyRequest()
+ response = home(request)
+ self.assertEqual(response.status_code, 200)
+ self.assertIn(b'Visit', response.body)
+
+ def test_hello(self):
+ from .views import hello
+
+ request = testing.DummyRequest()
+ response = hello(request)
+ self.assertEqual(response.status_code, 200)
+ self.assertIn(b'Go back', response.body)
+
+
+class TutorialFunctionalTests(unittest.TestCase):
+ def setUp(self):
+ from tutorial import main
+ app = main({})
+ from webtest import TestApp
+
+ self.testapp = TestApp(app)
+
+ def test_home(self):
+ res = self.testapp.get('/', status=200)
+ self.assertIn(b'<body>Visit', res.body)
+
+ def test_hello(self):
+ res = self.testapp.get('/howdy', status=200)
+ self.assertIn(b'<body>Go back', res.body)
diff --git a/docs/quick_tutorial/views/tutorial/views.py b/docs/quick_tutorial/views/tutorial/views.py
new file mode 100644
index 000000000..6ff149d7b
--- /dev/null
+++ b/docs/quick_tutorial/views/tutorial/views.py
@@ -0,0 +1,14 @@
+from pyramid.response import Response
+from pyramid.view import view_config
+
+
+# First view, available at http://localhost:6543/
+@view_config(route_name='home')
+def home(request):
+ return Response('<body>Visit <a href="/howdy">hello</a></body>')
+
+
+# /howdy
+@view_config(route_name='hello')
+def hello(request):
+ return Response('<body>Go back <a href="/">home</a></body>')
diff --git a/docs/tutorials/bfg/index.rst b/docs/tutorials/bfg/index.rst
deleted file mode 100644
index a50637279..000000000
--- a/docs/tutorials/bfg/index.rst
+++ /dev/null
@@ -1,204 +0,0 @@
-.. index::
- single: converting a BFG app
- single: bfg2pyramid
-
-.. _converting_a_bfg_app:
-
-Converting a :mod:`repoze.bfg` Application to :app:`Pyramid`
-============================================================
-
-Prior iterations of :app:`Pyramid` were released as a package named
-:mod:`repoze.bfg`. :mod:`repoze.bfg` users are encouraged to upgrade
-their deployments to :app:`Pyramid`, as, after the first final release
-of :app:`Pyramid`, further feature development on :mod:`repoze.bfg`
-will cease.
-
-Most existing :mod:`repoze.bfg` applications can be converted to a
-:app:`Pyramid` application in a completely automated fashion.
-However, if your application depends on packages which are not "core"
-parts of :mod:`repoze.bfg` but which nonetheless have ``repoze.bfg``
-in their names (e.g. ``repoze.bfg.skins``,
-``repoze.bfg.traversalwrapper``, ``repoze.bfg.jinja2``), you will need
-to find an analogue for each. For example, by the time you read this,
-there will be a ``pyramid_jinja2`` package, which can be used instead
-of ``repoze.bfg.jinja2``. If an analogue does not seem to exist for a
-``repoze.bfg`` add-on package that your application uses, please email
-the `Pylons-devel <http://groups.google.com/group/pylons-devel>`_
-maillist; we'll convert the package to a :app:`Pyramid` analogue for
-you.
-
-Here's how to convert a :mod:`repoze.bfg` application to a
-:app:`Pyramid` application:
-
-#. Ensure that your application works under :mod:`repoze.bfg` *version
- 1.3 or better*. See
- `http://docs.repoze.org/bfg/1.3/narr/install.html
- <http://docs.repoze.org/bfg/1.3/narr/install.html>`_ for
- :mod:`repoze.bfg` 1.3 installation instructions. If your
- application has an automated test suite, run it while your
- application is using :mod:`repoze.bfg` 1.3+. Otherwise, test it
- manually. It is only safe to proceed to the next step once your
- application works under :mod:`repoze.bfg` 1.3+.
-
- If your application has a proper set of dependencies, and a
- standard automated test suite, you might test your
- :mod:`repoze.bfg` application against :mod:`repoze.bfg` 1.3 like
- so:
-
- .. code-block:: bash
-
- $ $VENV/bin/python setup.py test
-
- ``bfgenv`` above will be the virtualenv into which you've installed
- :mod:`repoze.bfg` 1.3.
-
-#. Install :app:`Pyramid` into a *separate* virtualenv as per the
- instructions in :ref:`installing_chapter`. The :app:`Pyramid`
- virtualenv should be separate from the one you've used to install
- :mod:`repoze.bfg`. A quick way to do this:
-
- .. code-block:: bash
-
- $ cd ~
- $ virtualenv --no-site-packages pyramidenv
- $ cd pyramidenv
- $ $VENV/bin/easy_install pyramid
-
-#. Put a *copy* of your :mod:`repoze.bfg` application into a temporary
- location (perhaps by checking a fresh copy of the application out
- of a version control repository). For example:
-
- .. code-block:: bash
-
- $ cd /tmp
- $ svn co http://my.server/my/bfg/application/trunk bfgapp
-
-#. Use the ``bfg2pyramid`` script present in the ``bin`` directory of
- the :app:`Pyramid` virtualenv to convert all :mod:`repoze.bfg`
- Python import statements into compatible :app:`Pyramid` import
- statements. ``bfg2pyramid`` will also fix ZCML directive usages of
- common :mod:`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" file, representing your
- :mod:`repoze.bfg` application's setup script. ``bfg2pyramid`` will
- change the copy of the application *in place*.
-
- .. code-block:: bash
-
- $ ~/pyramidenv/bfg2pyramid /tmp/bfgapp
-
- ``bfg2pyramid`` will convert the following :mod:`repoze.bfg`
- application aspects to :app:`Pyramid` compatible analogues:
-
- - Python ``import`` statements naming :mod:`repoze.bfg` APIs will
- be converted to :app:`Pyramid` compatible ``import`` statements.
- Every Python file beneath the top-level path will be visited and
- converted recursively, except Python files which live in
- directories which start with a ``.`` (dot).
-
- - Each ZCML file 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 file beneath the
- top-level path (files ending with ``.zcml``) will be visited and
- converted recursively, except ZCML files which live in
- directories which start with a ``.`` (dot).
-
- - ZCML files 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 :app:`Pyramid` compatible ZCML attributes
- (e.g. ``context="pyramid.exceptions.NotFound``). Every ZCML file
- beneath the top-level path (files ending with ``.zcml``) will be
- visited and converted recursively, except ZCML files which live
- in directories which start with a ``.`` (dot).
-
-#. Edit the ``setup.py`` file of the application you've just converted
- (if you've 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 :mod:`repoze.bfg` application, you can do so by changing
- the ``requires`` line near the top of the ``setup.py`` file. The
- original may look like this:
-
- .. code-block:: text
-
- requires = ['repoze.bfg', ... other dependencies ...]
-
- Edit the ``setup.py`` so it has:
-
- .. code-block:: text
-
- requires = ['pyramid', ... other dependencies ...]
-
- All other install-requires and tests-requires dependencies save for
- the one on ``repoze.bfg`` can remain the same.
-
-#. Convert any ``install_requires`` dependencies your application has
- on other add-on packages which have ``repoze.bfg`` in their names
- to :app:`Pyramid` compatible analogues (e.g. ``repoze.bfg.jinja2``
- should be replaced with ``pyramid_jinja2``). You may need to
- adjust configuration options and/or imports in your
- :mod:`repoze.bfg` application after replacing these add-ons. Read
- the documentation of the :app:`Pyramid` add-on package for
- information.
-
-#. *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 define ZCML directives in the
- ``http://namespaces.repoze.org/bfg`` namespace will begin to "fail"
- (they're 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',
- u'http://namespaces.repoze.org/bfg', u'workflow')`` 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 define a namespace
- alias in the configure tag and prefix each failing tag. For
- example, change this "failing" tag instance::
-
- <configure xmlns="http://pylonshq.com/pyramid">
- <failingtag attr="foo"/>
- </configure>
-
- To this, which will begin to succeed::
-
- <configure xmlns="http://pylonshq.com/pyramid"
- xmlns:bfg="http://namespaces.repoze.org/bfg">
- <bfg:failingtag attr="foo"/>
- </configure>
-
- You will also need to add the ``pyramid_zcml`` package to your
- ``setup.py`` ``install_requires`` list. In Pyramid, ZCML configuration
- became an optional add-on supported by the ``pyramid_zcml`` package.
-
-#. Retest your application using :app:`Pyramid`. This might be as
- easy as:
-
- .. code-block:: bash
-
- $ cd /tmp/bfgapp
- $ $VENV/bin/python setup.py test
-
-#. Fix any test failures.
-
-#. Fix any code which generates deprecation warnings.
-
-#. 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 influenced server behavior which
-used to be prefixed with ``BFG_`` (such as ``BFG_DEBUG_NOTFOUND``) must now
-be prefixed with ``PYRAMID_``.
diff --git a/docs/tutorials/modwsgi/index.rst b/docs/tutorials/modwsgi/index.rst
index e0021f8db..ddd968927 100644
--- a/docs/tutorials/modwsgi/index.rst
+++ b/docs/tutorials/modwsgi/index.rst
@@ -46,7 +46,7 @@ specific path information for commands and files.
$ cd ~
$ mkdir modwsgi
$ cd modwsgi
- $ /usr/local/bin/virtualenv --no-site-packages env
+ $ /usr/local/bin/virtualenv env
#. Install :app:`Pyramid` into the newly created virtualenv:
diff --git a/docs/tutorials/wiki/basiclayout.rst b/docs/tutorials/wiki/basiclayout.rst
index 25ac9aabd..cdf52b73e 100644
--- a/docs/tutorials/wiki/basiclayout.rst
+++ b/docs/tutorials/wiki/basiclayout.rst
@@ -34,7 +34,10 @@ point happens to be the ``main`` function within the file named
factory` and the settings keywords parsed by :term:`PasteDeploy`. The root
factory is named ``root_factory``.
-#. *Line 15*. Register a "static view" which answers requests whose URL path
+#. *Line 15*. Include support for the :term:`Chameleon` template rendering
+ bindings, allowing us to use the ``.pt`` templates.
+
+#. *Line 16*. Register a "static view" which answers requests whose URL path
start with ``/static`` using the
:meth:`pyramid.config.Configurator.add_static_view` method. This
statement registers a view that will serve up static assets, such as CSS
@@ -47,7 +50,7 @@ point happens to be the ``main`` function within the file named
package. Alternatively the scaffold could have used an *absolute* asset
specification as the path (``tutorial:static``).
-#. *Line 16*. Perform a :term:`scan`. A scan will find :term:`configuration
+#. *Line 17*. Perform a :term:`scan`. A scan will find :term:`configuration
decoration`, such as view configuration decorators (e.g., ``@view_config``)
in the source code of the ``tutorial`` package and will take actions based
on these decorators. We don't pass any arguments to
@@ -56,7 +59,7 @@ point happens to be the ``main`` function within the file named
The scaffold could have equivalently said ``config.scan('tutorial')``, but
it chose to omit the package name argument.
-#. *Line 17*. Use the
+#. *Line 18*. Use the
:meth:`pyramid.config.Configurator.make_wsgi_app` method
to return a :term:`WSGI` application.
diff --git a/docs/tutorials/wiki/definingviews.rst b/docs/tutorials/wiki/definingviews.rst
index 23ee142af..e06468267 100644
--- a/docs/tutorials/wiki/definingviews.rst
+++ b/docs/tutorials/wiki/definingviews.rst
@@ -306,9 +306,9 @@ by the view (row 45). The view will use the ``body`` and
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 :ref:`chameleon_template_renderers` for
+ renderer is used. See :ref:`renderer_system_values` for
information about other names that are available by default
- when a Chameleon template is used as a renderer.
+ when a template is used as a renderer.
Static Assets
-------------
diff --git a/docs/tutorials/wiki/src/authorization/setup.py b/docs/tutorials/wiki/src/authorization/setup.py
index 5d87fedbf..5ab4f73cd 100644
--- a/docs/tutorials/wiki/src/authorization/setup.py
+++ b/docs/tutorials/wiki/src/authorization/setup.py
@@ -10,6 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
'pyramid_zodbconn',
'transaction',
'pyramid_tm',
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
index 8ea8f8fa3..39b94abd1 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/authorization/tutorial/__init__.py
@@ -21,6 +21,7 @@ def main(global_config, **settings):
config = Configurator(root_factory=root_factory, settings=settings)
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.scan()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
index e8672104d..13b41f823 100644
--- a/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/authorization/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/basiclayout/setup.py b/docs/tutorials/wiki/src/basiclayout/setup.py
index 75ba02611..da79881ab 100644
--- a/docs/tutorials/wiki/src/basiclayout/setup.py
+++ b/docs/tutorials/wiki/src/basiclayout/setup.py
@@ -10,6 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
'pyramid_zodbconn',
'transaction',
'pyramid_tm',
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
index c3bb87a62..f2a86df47 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/__init__.py
@@ -12,6 +12,7 @@ def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(root_factory=root_factory, settings=settings)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.scan()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
index e8672104d..13b41f823 100644
--- a/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/models/setup.py b/docs/tutorials/wiki/src/models/setup.py
index 75ba02611..da79881ab 100644
--- a/docs/tutorials/wiki/src/models/setup.py
+++ b/docs/tutorials/wiki/src/models/setup.py
@@ -10,6 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
'pyramid_zodbconn',
'transaction',
'pyramid_tm',
diff --git a/docs/tutorials/wiki/src/models/tutorial/__init__.py b/docs/tutorials/wiki/src/models/tutorial/__init__.py
index c3bb87a62..f2a86df47 100644
--- a/docs/tutorials/wiki/src/models/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/models/tutorial/__init__.py
@@ -12,6 +12,7 @@ def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(root_factory=root_factory, settings=settings)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.scan()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
index e8672104d..13b41f823 100644
--- a/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/models/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/models/tutorial/tests.py b/docs/tutorials/wiki/src/models/tutorial/tests.py
index 9fd13a18d..0c5f99575 100644
--- a/docs/tutorials/wiki/src/models/tutorial/tests.py
+++ b/docs/tutorials/wiki/src/models/tutorial/tests.py
@@ -45,7 +45,7 @@ class AppmakerTests(unittest.TestCase):
app_root = object()
root = {'app_root': app_root}
self._callFUT(root)
- self.failUnless(root['app_root'] is app_root)
+ self.assertTrue(root['app_root'] is app_root)
class ViewTests(unittest.TestCase):
def setUp(self):
diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py
index 5ff7b545c..2e7ed2398 100644
--- a/docs/tutorials/wiki/src/tests/setup.py
+++ b/docs/tutorials/wiki/src/tests/setup.py
@@ -10,6 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
'pyramid_zodbconn',
'transaction',
'pyramid_tm',
diff --git a/docs/tutorials/wiki/src/tests/tutorial/__init__.py b/docs/tutorials/wiki/src/tests/tutorial/__init__.py
index 8ea8f8fa3..bd3c5619f 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/__init__.py
@@ -19,6 +19,7 @@ def main(global_config, **settings):
'sosecret', callback=groupfinder, hashalg='sha512')
authz_policy = ACLAuthorizationPolicy()
config = Configurator(root_factory=root_factory, settings=settings)
+ config.include('pyramid_chameleon')
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.add_static_view('static', 'static', cache_max_age=3600)
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
index e8672104d..13b41f823 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki/src/views/setup.py b/docs/tutorials/wiki/src/views/setup.py
index 5d87fedbf..5ab4f73cd 100644
--- a/docs/tutorials/wiki/src/views/setup.py
+++ b/docs/tutorials/wiki/src/views/setup.py
@@ -10,6 +10,7 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
'pyramid_zodbconn',
'transaction',
'pyramid_tm',
diff --git a/docs/tutorials/wiki/src/views/tutorial/__init__.py b/docs/tutorials/wiki/src/views/tutorial/__init__.py
index c3bb87a62..f2a86df47 100644
--- a/docs/tutorials/wiki/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/views/tutorial/__init__.py
@@ -12,6 +12,7 @@ def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(root_factory=root_factory, settings=settings)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.scan()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
index 84824f605..50102aa20 100644
--- a/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki/src/views/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki/tests.rst b/docs/tutorials/wiki/tests.rst
index e40dc286b..e724f3e18 100644
--- a/docs/tutorials/wiki/tests.rst
+++ b/docs/tutorials/wiki/tests.rst
@@ -59,8 +59,8 @@ Change the ``requires`` list in ``setup.py`` to include ``WebTest``.
.. literalinclude:: src/tests/setup.py
:linenos:
:language: python
- :lines: 11-21
- :emphasize-lines: 10
+ :lines: 11-22
+ :emphasize-lines: 11
After we've added a dependency on WebTest in ``setup.py``, we need to rerun
``setup.py develop`` to get WebTest installed into our virtualenv. Assuming
diff --git a/docs/tutorials/wiki2/authorization.rst b/docs/tutorials/wiki2/authorization.rst
index 01c301e74..cf20db6d7 100644
--- a/docs/tutorials/wiki2/authorization.rst
+++ b/docs/tutorials/wiki2/authorization.rst
@@ -83,7 +83,7 @@ statement at the head:
Add the following class definition:
.. literalinclude:: src/authorization/tutorial/models.py
- :lines: 36-40
+ :lines: 33-37
:linenos:
:language: python
@@ -203,7 +203,7 @@ Go back to ``tutorial/tutorial/__init__.py`` and add these two
routes:
.. literalinclude:: src/authorization/tutorial/__init__.py
- :lines: 30-31
+ :lines: 31-32
:linenos:
:language: python
@@ -329,7 +329,7 @@ when we're done:
.. literalinclude:: src/authorization/tutorial/__init__.py
:linenos:
- :emphasize-lines: 2-3,7,21-23,25-27,30-31
+ :emphasize-lines: 2-3,7,21-23,25-27,31-32
:language: python
(Only the highlighted lines need to be added.)
@@ -339,7 +339,7 @@ when we're done:
.. literalinclude:: src/authorization/tutorial/models.py
:linenos:
- :emphasize-lines: 1-4,36-40
+ :emphasize-lines: 1-4,33-37
:language: python
(Only the highlighted lines need to be added.)
diff --git a/docs/tutorials/wiki2/basiclayout.rst b/docs/tutorials/wiki2/basiclayout.rst
index 0193afab4..05781c044 100644
--- a/docs/tutorials/wiki2/basiclayout.rst
+++ b/docs/tutorials/wiki2/basiclayout.rst
@@ -82,11 +82,18 @@ dictionary of settings parsed from the ``.ini`` file, which contains
deployment-related values such as ``pyramid.reload_templates``,
``db_string``, etc.
+Next, include :term:`Chameleon` templating bindings so that we can use
+renderers with the ``.pt`` extension within our project.
+
+ .. literalinclude:: src/basiclayout/tutorial/__init__.py
+ :lines: 17
+ :language: py
+
``main`` now calls :meth:`pyramid.config.Configurator.add_static_view` with
two arguments: ``static`` (the name), and ``static`` (the path):
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 17
+ :lines: 18
:language: py
This registers a static resource view which will match any URL that starts
@@ -104,7 +111,7 @@ via the :meth:`pyramid.config.Configurator.add_route` method that will be
used when the URL is ``/``:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 18
+ :lines: 19
:language: py
Since this route has a ``pattern`` equalling ``/`` it is the route that will
@@ -118,7 +125,7 @@ view configuration will be registered, which will allow one of our
application URLs to be mapped to some code.
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 19
+ :lines: 20
:language: py
Finally, ``main`` is finished configuring things, so it uses the
@@ -126,7 +133,7 @@ Finally, ``main`` is finished configuring things, so it uses the
:term:`WSGI` application:
.. literalinclude:: src/basiclayout/tutorial/__init__.py
- :lines: 20
+ :lines: 21
:language: py
View Declarations via ``views.py``
@@ -225,10 +232,17 @@ To give a simple example of a model class, we define one named ``MyModel``:
:linenos:
:language: py
-Our example model has an ``__init__`` method that takes two arguments
-(``name``, and ``value``). It stores these values as ``self.name`` and
-``self.value`` on the instance created by the ``__init__`` function itself.
-The ``MyModel`` class also has a ``__tablename__`` attribute. This informs
+Our example model does not require an ``__init__`` method because SQLAlchemy
+supplies for us a default constructor if one is not already present,
+which accepts keyword arguments of the same name as that of the mapped attributes.
+
+.. note:: Example usage of MyModel:
+
+ .. code-block:: python
+
+ johnny = MyModel(name="John Doe", value=10)
+
+The ``MyModel`` class has a ``__tablename__`` attribute. This informs
SQLAlchemy which table to use to store the data representing instances of this
class.
diff --git a/docs/tutorials/wiki2/definingmodels.rst b/docs/tutorials/wiki2/definingmodels.rst
index 60427a911..e30af12b2 100644
--- a/docs/tutorials/wiki2/definingmodels.rst
+++ b/docs/tutorials/wiki2/definingmodels.rst
@@ -24,7 +24,7 @@ following:
.. literalinclude:: src/models/tutorial/models.py
:linenos:
:language: py
- :emphasize-lines: 20-22,25,27,29
+ :emphasize-lines: 20-22,25
(The highlighted lines are the ones that need to be changed.)
diff --git a/docs/tutorials/wiki2/definingviews.rst b/docs/tutorials/wiki2/definingviews.rst
index a1e2313f3..49dbed50f 100644
--- a/docs/tutorials/wiki2/definingviews.rst
+++ b/docs/tutorials/wiki2/definingviews.rst
@@ -30,7 +30,7 @@ Open ``tutorial/setup.py`` and edit it to look like the following:
.. literalinclude:: src/views/setup.py
:linenos:
:language: python
- :emphasize-lines: 19
+ :emphasize-lines: 20
(Only the highlighted line needs to be added.)
@@ -272,9 +272,9 @@ by the view (row 45). The view will use the ``body`` and
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 :ref:`chameleon_template_renderers` for
+ renderer is used. See :ref:`renderer_system_values` for
information about other names that are available by default
- when a Chameleon template is used as a renderer.
+ when a template is used as a renderer.
Static Assets
-------------
@@ -335,7 +335,7 @@ something like:
.. literalinclude:: src/views/tutorial/__init__.py
:linenos:
:language: python
- :emphasize-lines: 18-21
+ :emphasize-lines: 19-22
(The highlighted lines are the ones that need to be added or edited.)
diff --git a/docs/tutorials/wiki2/installation.rst b/docs/tutorials/wiki2/installation.rst
index 17788cdde..e21bf7108 100644
--- a/docs/tutorials/wiki2/installation.rst
+++ b/docs/tutorials/wiki2/installation.rst
@@ -26,7 +26,7 @@ On UNIX
.. code-block:: text
$ export VENV=~/pyramidtut
- $ virtualenv --no-site-packages $VENV
+ $ virtualenv $VENV
New python executable in /home/foo/env/bin/python
Installing setuptools.............done.
@@ -46,13 +46,13 @@ Python 2.7:
.. code-block:: text
- c:\> c:\Python27\Scripts\virtualenv --no-site-packages %VENV%
+ c:\> c:\Python27\Scripts\virtualenv %VENV%
Python 3.2:
.. code-block:: text
- c:\> c:\Python32\Scripts\virtualenv --no-site-packages %VENV%
+ c:\> c:\Python32\Scripts\virtualenv %VENV%
Install Pyramid Into the Virtual Python Environment
---------------------------------------------------
diff --git a/docs/tutorials/wiki2/src/authorization/setup.py b/docs/tutorials/wiki2/src/authorization/setup.py
index e8fa8f396..09bd63d33 100644
--- a/docs/tutorials/wiki2/src/authorization/setup.py
+++ b/docs/tutorials/wiki2/src/authorization/setup.py
@@ -10,10 +10,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'SQLAlchemy',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'zope.sqlalchemy',
'waitress',
'docutils',
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
index d08e55bf9..2ada42171 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/__init__.py
@@ -25,6 +25,7 @@ def main(global_config, **settings):
root_factory='tutorial.models.RootFactory')
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
config.add_route('login', '/login')
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/models.py b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
index 91e5a0019..4f7e1e024 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/models.py
@@ -29,9 +29,6 @@ class Page(Base):
name = Column(Text, unique=True)
data = Column(Text)
- def __init__(self, name, data):
- self.name = name
- self.data = data
class RootFactory(object):
__acl__ = [ (Allow, Everyone, 'view'),
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py
index 092e359ce..23a5f13f4 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/scripts/initializedb.py
@@ -33,5 +33,5 @@ def main(argv=sys.argv):
DBSession.configure(bind=engine)
Base.metadata.create_all(engine)
with transaction.manager:
- model = Page('FrontPage', 'This is the front page')
+ model = Page(name='FrontPage', data='This is the front page')
DBSession.add(model)
diff --git a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
index 14b88d16a..cf3da2073 100644
--- a/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/authorization/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/basiclayout/setup.py b/docs/tutorials/wiki2/src/basiclayout/setup.py
index e7d318128..15e7e5923 100644
--- a/docs/tutorials/wiki2/src/basiclayout/setup.py
+++ b/docs/tutorials/wiki2/src/basiclayout/setup.py
@@ -10,10 +10,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'SQLAlchemy',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'zope.sqlalchemy',
'waitress',
]
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
index aac7c5e69..867049e4f 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/__init__.py
@@ -14,6 +14,7 @@ def main(global_config, **settings):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.scan()
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
index aeeb9df64..0cdd4bbc3 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/models.py
@@ -22,7 +22,3 @@ class MyModel(Base):
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
diff --git a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
index ee9fdb7fa..ca4e0af26 100644
--- a/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/basiclayout/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/models/setup.py b/docs/tutorials/wiki2/src/models/setup.py
index e7d318128..15e7e5923 100644
--- a/docs/tutorials/wiki2/src/models/setup.py
+++ b/docs/tutorials/wiki2/src/models/setup.py
@@ -10,10 +10,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'SQLAlchemy',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'zope.sqlalchemy',
'waitress',
]
diff --git a/docs/tutorials/wiki2/src/models/tutorial/__init__.py b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
index aac7c5e69..867049e4f 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/__init__.py
@@ -14,6 +14,7 @@ def main(global_config, **settings):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('home', '/')
config.scan()
diff --git a/docs/tutorials/wiki2/src/models/tutorial/models.py b/docs/tutorials/wiki2/src/models/tutorial/models.py
index 9a078d757..f028c917a 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/models.py
@@ -23,7 +23,3 @@ class Page(Base):
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
diff --git a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py
index 092e359ce..23a5f13f4 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/models/tutorial/scripts/initializedb.py
@@ -33,5 +33,5 @@ def main(argv=sys.argv):
DBSession.configure(bind=engine)
Base.metadata.create_all(engine)
with transaction.manager:
- model = Page('FrontPage', 'This is the front page')
+ model = Page(name='FrontPage', data='This is the front page')
DBSession.add(model)
diff --git a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
index ee9fdb7fa..ca4e0af26 100644
--- a/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/models/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py
index c3da36b39..d8486e462 100644
--- a/docs/tutorials/wiki2/src/tests/setup.py
+++ b/docs/tutorials/wiki2/src/tests/setup.py
@@ -10,10 +10,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'SQLAlchemy',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'zope.sqlalchemy',
'waitress',
'docutils',
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
index d08e55bf9..cee89184b 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py
@@ -23,6 +23,7 @@ def main(global_config, **settings):
authz_policy = ACLAuthorizationPolicy()
config = Configurator(settings=settings,
root_factory='tutorial.models.RootFactory')
+ config.include('pyramid_chameleon')
config.set_authentication_policy(authn_policy)
config.set_authorization_policy(authz_policy)
config.add_static_view('static', 'static', cache_max_age=3600)
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models.py b/docs/tutorials/wiki2/src/tests/tutorial/models.py
index 91e5a0019..4f7e1e024 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/models.py
@@ -29,9 +29,6 @@ class Page(Base):
name = Column(Text, unique=True)
data = Column(Text)
- def __init__(self, name, data):
- self.name = name
- self.data = data
class RootFactory(object):
__acl__ = [ (Allow, Everyone, 'view'),
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
index 092e359ce..23a5f13f4 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/tests/tutorial/scripts/initializedb.py
@@ -33,5 +33,5 @@ def main(argv=sys.argv):
DBSession.configure(bind=engine)
Base.metadata.create_all(engine)
with transaction.manager:
- model = Page('FrontPage', 'This is the front page')
+ model = Page(name='FrontPage', data='This is the front page')
DBSession.add(model)
diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
index 9c077568d..6c1ca924a 100644
--- a/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/src/views/setup.py b/docs/tutorials/wiki2/src/views/setup.py
index e8fa8f396..09bd63d33 100644
--- a/docs/tutorials/wiki2/src/views/setup.py
+++ b/docs/tutorials/wiki2/src/views/setup.py
@@ -10,10 +10,11 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
requires = [
'pyramid',
+ 'pyramid_chameleon',
+ 'pyramid_debugtoolbar',
+ 'pyramid_tm',
'SQLAlchemy',
'transaction',
- 'pyramid_tm',
- 'pyramid_debugtoolbar',
'zope.sqlalchemy',
'waitress',
'docutils',
diff --git a/docs/tutorials/wiki2/src/views/tutorial/__init__.py b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
index c95bfdbf8..37cae1997 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/__init__.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/__init__.py
@@ -14,6 +14,7 @@ def main(global_config, **settings):
DBSession.configure(bind=engine)
Base.metadata.bind = engine
config = Configurator(settings=settings)
+ config.include('pyramid_chameleon')
config.add_static_view('static', 'static', cache_max_age=3600)
config.add_route('view_wiki', '/')
config.add_route('view_page', '/{pagename}')
diff --git a/docs/tutorials/wiki2/src/views/tutorial/models.py b/docs/tutorials/wiki2/src/views/tutorial/models.py
index 9a078d757..f028c917a 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/models.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/models.py
@@ -23,7 +23,3 @@ class Page(Base):
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
diff --git a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py
index 092e359ce..23a5f13f4 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py
+++ b/docs/tutorials/wiki2/src/views/tutorial/scripts/initializedb.py
@@ -33,5 +33,5 @@ def main(argv=sys.argv):
DBSession.configure(bind=engine)
Base.metadata.create_all(engine)
with transaction.manager:
- model = Page('FrontPage', 'This is the front page')
+ model = Page(name='FrontPage', data='This is the front page')
DBSession.add(model)
diff --git a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
index ee9fdb7fa..ca4e0af26 100644
--- a/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
+++ b/docs/tutorials/wiki2/src/views/tutorial/templates/mytemplate.pt
@@ -1,7 +1,7 @@
<!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>The Pyramid Web Application Development Framework</title>
+ <title>The Pyramid Web Framework</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" />
@@ -24,7 +24,7 @@
<div class="middle align-center">
<p class="app-welcome">
Welcome to <span class="app-name">${project}</span>, an application generated by<br/>
- the Pyramid web application development framework.
+ the Pyramid Web Framework.
</p>
</div>
</div>
diff --git a/docs/tutorials/wiki2/tests.rst b/docs/tutorials/wiki2/tests.rst
index 33b5d35c1..9aca0c5b7 100644
--- a/docs/tutorials/wiki2/tests.rst
+++ b/docs/tutorials/wiki2/tests.rst
@@ -54,8 +54,8 @@ Change the ``requires`` list in ``setup.py`` to include ``WebTest``.
.. literalinclude:: src/tests/setup.py
:linenos:
:language: python
- :lines: 11-21
- :emphasize-lines: 10
+ :lines: 11-22
+ :emphasize-lines: 11
After we've added a dependency on WebTest in ``setup.py``, we need to rerun
``setup.py develop`` to get WebTest installed into our virtualenv. Assuming
diff --git a/docs/whatsnew-1.0.rst b/docs/whatsnew-1.0.rst
index d1f3046ca..8750863e7 100644
--- a/docs/whatsnew-1.0.rst
+++ b/docs/whatsnew-1.0.rst
@@ -92,7 +92,7 @@ BFG Conversion Script
The ``bfg2pyramid`` conversion script performs a mostly automated conversion
of an existing :mod:`repoze.bfg` application to Pyramid. The process is
-described in :ref:`converting_a_bfg_app`.
+described in "Converting a BFG Application to Pyramid".
Scaffold Improvements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -203,8 +203,8 @@ Mako
~~~~
In addition to Chameleon templating, Pyramid now also provides built-in
-support for :term:`Mako` templating. See :ref:`mako_templates` for more
-information.
+support for :term:`Mako` templating. See
+:ref:`available_template_system_bindings` for more information.
URL Dispatch
~~~~~~~~~~~~
diff --git a/docs/whatsnew-1.1.rst b/docs/whatsnew-1.1.rst
index 5cba8dd3e..086c12ca2 100644
--- a/docs/whatsnew-1.1.rst
+++ b/docs/whatsnew-1.1.rst
@@ -13,7 +13,7 @@ Terminology Changes
The term "template" used by the Pyramid documentation used to refer to both
"paster templates" and "rendered templates" (templates created by a rendering
engine. i.e. Mako, Chameleon, Jinja, etc.). "Paster templates" will now be
-refered to as "scaffolds", whereas the name for "rendered templates" will
+referred to as "scaffolds", whereas the name for "rendered templates" will
remain as "templates."
Major Feature Additions
@@ -397,7 +397,7 @@ Deprecations and Behavior Differences
shell you use to invoke ``paster serve`` to see these warnings, e.g. on
UNIX, ``PYTHONWARNINGS=all $VENV/bin/paster serve development.ini``.
Python 2.5 and 2.6 show deprecation warnings by default,
- so this is unecessary there.
+ so this is unnecessary there.
All deprecation warnings are emitted to the console.
- The :class:`pyramid.view.static` class has been deprecated in favor of the
@@ -540,11 +540,10 @@ Deprecations and Behavior Differences
within a static view returns the index.html properly. See also
https://github.com/Pylons/pyramid/issues/67.
-- Deprecated the
- :meth:`pyramid.config.Configurator.set_renderer_globals_factory` method and
- the ``renderer_globals`` Configurator constructor parameter. Users should
- convert code using this feature to use a BeforeRender event. See the section
- :ref:`beforerender_event` in the Hooks chapter.
+- Deprecated the ``pyramid.config.Configurator.set_renderer_globals_factory``
+ method and the ``renderer_globals`` Configurator constructor parameter.
+ Users should convert code using this feature to use a BeforeRender event. See
+ the section :ref:`beforerender_event` in the Hooks chapter.
- In Pyramid 1.0, the :class:`pyramid.events.subscriber` directive behaved
contrary to the documentation when passed more than one interface object to
diff --git a/docs/whatsnew-1.5.rst b/docs/whatsnew-1.5.rst
new file mode 100644
index 000000000..57f93cbff
--- /dev/null
+++ b/docs/whatsnew-1.5.rst
@@ -0,0 +1,433 @@
+What's New In Pyramid 1.5
+=========================
+
+This article explains the new features in :app:`Pyramid` version 1.5 as
+compared to its predecessor, :app:`Pyramid` 1.4. It also documents backwards
+incompatibilities between the two versions and deprecations added to
+:app:`Pyramid` 1.5, as well as software dependency changes and notable
+documentation additions.
+
+Major Backwards Incompatibilities
+---------------------------------
+
+- Pyramid no longer depends on or configures the Mako and Chameleon templating
+ system renderers by default. Disincluding these templating systems by
+ default means that the Pyramid core has fewer dependencies and can run on
+ future platforms without immediate concern for the compatibility of its
+ templating add-ons. It also makes maintenance slightly more effective, as
+ different people can maintain the templating system add-ons that they
+ understand and care about without needing commit access to the Pyramid core,
+ and it allows users who just don't want to see any packages they don't use
+ come along for the ride when they install Pyramid.
+
+ This means that upon upgrading to Pyramid 1.5a2+, projects that use either
+ of these templating systems will see a traceback that ends something like
+ this when their application attempts to render a Chameleon or Mako template::
+
+ ValueError: No such renderer factory .pt
+
+ Or::
+
+ ValueError: No such renderer factory .mako
+
+ Or::
+
+ ValueError: No such renderer factory .mak
+
+ Support for Mako templating has been moved into an add-on package named
+ ``pyramid_mako``, and support for Chameleon templating has been moved into
+ an add-on package named ``pyramid_chameleon``. These packages are drop-in
+ replacements for the old built-in support for these templating langauges.
+ All you have to do is install them and make them active in your configuration
+ to register renderer factories for ``.pt`` and/or ``.mako`` (or ``.mak``) to
+ make your application work again.
+
+ To re-add support for Chameleon and/or Mako template renderers into your
+ existing projects, follow the below steps.
+
+ If you depend on Mako templates:
+
+ * Make sure the ``pyramid_mako`` package is installed. One way to do this
+ is by adding ``pyramid_mako`` to the ``install_requires`` section of your
+ package's ``setup.py`` file and afterwards rerunning ``setup.py develop``::
+
+ setup(
+ #...
+ install_requires=[
+ 'pyramid_mako', # new dependency
+ 'pyramid',
+ #...
+ ],
+ )
+
+ * Within the portion of your application which instantiates a Pyramid
+ :class:`~pyramid.config.Configurator` (often the ``main()`` function in
+ your project's ``__init__.py`` file), tell Pyramid to include the
+ ``pyramid_mako`` includeme::
+
+ config = Configurator(.....)
+ config.include('pyramid_mako')
+
+ If you depend on Chameleon templates:
+
+ * Make sure the ``pyramid_chameleon`` package is installed. One way to do
+ this is by adding ``pyramid_chameleon`` to the ``install_requires`` section
+ of your package's ``setup.py`` file and afterwards rerunning
+ ``setup.py develop``::
+
+ setup(
+ #...
+ install_requires=[
+ 'pyramid_chameleon', # new dependency
+ 'pyramid',
+ #...
+ ],
+ )
+
+ * Within the portion of your application which instantiates a Pyramid
+ :class:`~pyramid.config.Configurator` (often the ``main()`` function in
+ your project's ``__init__.py`` file), tell Pyramid to include the
+ ``pyramid_chameleon`` includeme::
+
+ config = Configurator(.....)
+ config.include('pyramid_chameleon')
+
+ Note that it's also fine to install these packages into *older* Pyramids for
+ forward compatibility purposes. Even if you don't upgrade to Pyramid 1.5
+ immediately, performing the above steps in a Pyramid 1.4 installation is
+ perfectly fine, won't cause any difference, and will give you forward
+ compatibility when you eventually do upgrade to Pyramid 1.5.
+
+ With the removal of Mako and Chameleon support from the core, some
+ unit tests that use the ``pyramid.renderers.render*`` methods may begin to
+ fail. If any of your unit tests are invoking either
+ ``pyramid.renderers.render()`` or ``pyramid.renderers.render_to_response()``
+ with either Mako or Chameleon templates then the
+ ``pyramid.config.Configurator`` instance in effect during
+ the unit test should be also be updated to include the addons, as shown
+ above. For example::
+
+ class ATest(unittest.TestCase):
+ def setUp(self):
+ self.config = pyramid.testing.setUp()
+ self.config.include('pyramid_mako')
+
+ def test_it(self):
+ result = pyramid.renderers.render('mypkg:templates/home.mako', {})
+
+ Or::
+
+ class ATest(unittest.TestCase):
+ def setUp(self):
+ self.config = pyramid.testing.setUp()
+ self.config.include('pyramid_chameleon')
+
+ def test_it(self):
+ result = pyramid.renderers.render('mypkg:templates/home.pt', {})
+
+- If you're using the Pyramid debug toolbar, when you upgrade Pyramid to
+ 1.5a2+, you'll also need to upgrade the ``pyramid_debugtoolbar`` package to
+ at least version 1.0.8, as older toolbar versions are not compatible with
+ Pyramid 1.5a2+ due to the removal of Mako support from the core. It's
+ fine to use this newer version of the toolbar code with older Pyramids too.
+
+Feature Additions
+-----------------
+
+The feature additions in Pyramid 1.5 follow.
+
+- Add ``pdistreport`` script, which prints the Python version in use, the
+ Pyramid version in use, and the version number and location of all Python
+ distributions currently installed.
+
+- Add the ability to invert the result of any view, route, or subscriber
+ predicate value using the ``not_`` class. For example:
+
+ .. code-block:: python
+
+ from pyramid.config import not_
+
+ @view_config(route_name='myroute', request_method=not_('POST'))
+ def myview(request): ...
+
+ The above example will ensure that the view is called if the request method
+ is not POST, at least if no other view is more specific.
+
+ The :class:`pyramid.config.not_` class can be used against any value that is
+ a predicate value passed in any of these contexts:
+
+ - :meth:`pyramid.config.Configurator.add_view`
+
+ - :meth:`pyramid.config.Configurator.add_route`
+
+ - :meth:`pyramid.config.Configurator.add_subscriber`
+
+ - :meth:`pyramid.view.view_config`
+
+ - :meth:`pyramid.events.subscriber`
+
+- View lookup will now search for valid views based on the inheritance
+ hierarchy of the context. It tries to find views based on the most specific
+ context first, and upon predicate failure, will move up the inheritance chain
+ to test views found by the super-type of the context. In the past, only the
+ most specific type containing views would be checked and if no matching view
+ could be found then a PredicateMismatch would be raised. Now predicate
+ mismatches don't hide valid views registered on super-types. Here's an
+ example that now works:
+
+ .. code-block:: python
+
+ class IResource(Interface):
+
+ ...
+
+ @view_config(context=IResource)
+ def get(context, request):
+
+ ...
+
+ @view_config(context=IResource, request_method='POST')
+ def post(context, request):
+
+ ...
+
+ @view_config(context=IResource, request_method='DELETE')
+ def delete(context, request):
+
+ ...
+
+ @implementer(IResource)
+ class MyResource:
+
+ ...
+
+ @view_config(context=MyResource, request_method='POST')
+ def override_post(context, request):
+
+ ...
+
+ Previously the override_post view registration would hide the get
+ and delete views in the context of MyResource -- leading to a
+ predicate mismatch error when trying to use GET or DELETE
+ methods. Now the views are found and no predicate mismatch is
+ raised.
+ See https://github.com/Pylons/pyramid/pull/786 and
+ https://github.com/Pylons/pyramid/pull/1004 and
+ https://github.com/Pylons/pyramid/pull/1046
+
+- ``scripts/prequest.py`` (aka the ``prequest`` console script): added support
+ for submitting ``PUT`` and ``PATCH`` requests. See
+ https://github.com/Pylons/pyramid/pull/1033. add support for submitting
+ ``OPTIONS`` and ``PROPFIND`` requests, and allow users to specify basic
+ authentication credentials in the request via a ``--login`` argument to the
+ script. See https://github.com/Pylons/pyramid/pull/1039.
+
+- The :meth:`pyramid.config.Configurator.add_route` method now supports being
+ called with an external URL as pattern. See
+ https://github.com/Pylons/pyramid/issues/611 and the documentation section
+ :ref:`external_route_narr`.
+
+- :class:`pyramid.authorization.ACLAuthorizationPolicy` supports ``__acl__`` as
+ a callable. This removes the ambiguity between the potential
+ ``AttributeError`` that would be raised on the ``context`` when the property
+ was not defined and the ``AttributeError`` that could be raised from any
+ user-defined code within a dynamic property. It is recommended to define a
+ dynamic ACL as a callable to avoid this ambiguity. See
+ https://github.com/Pylons/pyramid/issues/735.
+
+- Allow a protocol-relative URL (e.g. ``//example.com/images``) to be passed to
+ :meth:`pyramid.config.Configurator.add_static_view`. This allows
+ externally-hosted static URLs to be generated based on the current protocol.
+
+- The :class:`pyramid.authentication.AuthTktAuthenticationPolicy` class has two
+ new options to configure its domain usage:
+
+ * ``parent_domain``: if set the authentication cookie is set on
+ the parent domain. This is useful if you have multiple sites sharing the
+ same domain.
+
+ * ``domain``: if provided the cookie is always set for this domain, bypassing
+ all usual logic.
+
+ See https://github.com/Pylons/pyramid/pull/1028,
+ https://github.com/Pylons/pyramid/pull/1072 and
+ https://github.com/Pylons/pyramid/pull/1078.
+
+- The :class:`pyramid.authentication.AuthTktPolicy` now supports IPv6
+ addresses when using the ``include_ip=True`` option. This is possibly
+ incompatible with alternative ``auth_tkt`` implementations, as the
+ specification does not define how to properly handle IPv6. See
+ https://github.com/Pylons/pyramid/issues/831.
+
+- Make it possible to use variable arguments via
+ :func:`pyramid.paster.get_appsettings`. This also allowed the generated
+ ``initialize_db`` script from the ``alchemy`` scaffold to grow support for
+ options in the form ``a=1 b=2`` so you can fill in values in a parameterized
+ ``.ini`` file, e.g. ``initialize_myapp_db etc/development.ini a=1 b=2``.
+ See https://github.com/Pylons/pyramid/pull/911
+
+- The ``request.session.check_csrf_token()`` method and the ``check_csrf`` view
+ predicate now take into account the value of the HTTP header named
+ ``X-CSRF-Token`` (as well as the ``csrf_token`` form parameter, which they
+ always did). The header is tried when the form parameter does not exist.
+
+- You can now generate "hybrid" urldispatch/traversal URLs more easily by using
+ the new ``route_name``, ``route_kw`` and ``route_remainder_name`` arguments
+ to :meth:`~pyramid.request.Request.resource_url` and
+ :meth:`~pyuramid.request.Request.resource_path`. See
+ :ref:`generating_hybrid_urls`.
+
+- A new http exception superclass named
+ :class:`~pyramid.httpexceptions.HTTPSuccessful` was added. You can use this
+ class as the ``context`` of an exception view to catch all 200-series
+ "exceptions" (e.g. "raise HTTPOk"). This also allows you to catch *only* the
+ :class:`~pyramid.httpexceptions.HTTPOk` exception itself; previously this was
+ impossible because a number of other exceptions (such as ``HTTPNoContent``)
+ inherited from ``HTTPOk``, but now they do not.
+
+- It is now possible to escape double braces in Pyramid scaffolds (unescaped,
+ these represent replacement values). You can use ``\{\{a\}\}`` to
+ represent a "bare" ``{{a}}``. See
+ https://github.com/Pylons/pyramid/pull/862
+
+- Add ``localizer`` and ``locale_name`` properties (reified) to
+ :class:`pyramid.request.Request`. See
+ https://github.com/Pylons/pyramid/issues/508. Note that the
+ :func:`pyramid.i18n.get_localizer` and :func:`pyramid.i18n.get_locale_name`
+ functions now simply look up these properties on the request.
+
+- The ``pserve`` command now takes a ``-v`` (or ``--verbose``) flag and a
+ ``-q`` (or ``--quiet``) flag. Output from running ``pserve`` can be
+ controlled using these flags. ``-v`` can be specified multiple times to
+ increase verbosity. ``-q`` sets verbosity to ``0`` unconditionally. The
+ default verbosity level is ``1``.
+
+- The ``alchemy`` scaffold tests now provide better coverage. See
+ https://github.com/Pylons/pyramid/pull/1029
+
+- Users can now provide dotted Python names to as the ``factory`` argument
+ the Configurator methods named
+ :meth:`~pyramid.config.Configurator.add_view_predicate`,
+ :meth:`~pyramid.config.Configurator.add_route_predicate` and
+ :meth:`~pyramid.config.Configurator.add_subscriber_predicate`. Instead of
+ passing the predicate factory directly, you can pass a dotted name which
+ refers to the factory.
+
+- :func:`pyramid.path.package_name` no longer thows an exception when resolving
+ the package name for namespace packages that have no ``__file__`` attribute.
+
+Other Backwards Incompatibilities
+---------------------------------
+
+- Modified the :meth:`~pyramid.request.Reuqest.current_route_url` method. The
+ method previously returned the URL without the query string by default, it
+ now does attach the query string unless it is overriden.
+
+- The :meth:`~pyramid.request.Request.route_url` and
+ :meth:`~pyramid.request.Request.route_path` APIs no longer quote ``/`` to
+ ``%2F`` when a replacement value contains a ``/``. This was pointless, as
+ WSGI servers always unquote the slash anyway, and Pyramid never sees the
+ quoted value.
+
+- It is no longer possible to set a ``locale_name`` attribute of the request,
+ nor is it possible to set a ``localizer`` attribute of the request. These
+ are now "reified" properties that look up a locale name and localizer
+ respectively using the machinery described in :ref:`i18n_chapter`.
+
+- If you send an ``X-Vhm-Root`` header with a value that ends with a slash (or
+ any number of slashes), the trailing slash(es) will be removed before a URL
+ is generated when you use use :meth:`~pyramid.request.Request.resource_url`
+ or :meth:`~pyramid.request.Request.resource_path`. Previously the virtual
+ root path would not have trailing slashes stripped, which would influence URL
+ generation.
+
+- The :class:`pyramid.interfaces.IResourceURL` interface has now grown two new
+ attributes: ``virtual_path_tuple`` and ``physical_path_tuple``. These should
+ be the tuple form of the resource's path (physical and virtual).
+
+- Removed the ``request.response_*`` varying attributes (such
+ as``request.response_headers``) . These attributes had been deprecated
+ since Pyramid 1.1, and as per the deprecation policy, have now been removed.
+
+- ``request.response`` will no longer be mutated when using the
+ :func:`pyramid.renderers.render` API. Almost all renderers mutate the
+ ``request.response`` response object (for example, the JSON renderer sets
+ ``request.response.content_type`` to ``application/json``), but this is
+ only necessary when the renderer is generating a response; it was a bug
+ when it was done as a side effect of calling
+ :func:`pyramid.renderers.render`.
+
+- Removed the ``bfg2pyramid`` fixer script.
+
+- The :class:`pyramid.events.NewResponse` event is now sent **after** response
+ callbacks are executed. It previously executed before response callbacks
+ were executed. Rationale: it's more useful to be able to inspect the response
+ after response callbacks have done their jobs instead of before.
+
+- Removed the class named ``pyramid.view.static`` that had been deprecated
+ since Pyramid 1.1. Instead use :class:`pyramid.static.static_view` with the
+ ``use_subpath=True`` argument.
+
+- Removed the ``pyramid.view.is_response`` function that had been deprecated
+ since Pyramid 1.1. Use the :meth:`pyramid.request.Request.is_response`
+ method instead.
+
+- Removed the ability to pass the following arguments to
+ :meth:`pyramid.config.Configurator.add_route`: ``view``, ``view_context``.
+ ``view_for``, ``view_permission``, ``view_renderer``, and ``view_attr``.
+ Using these arguments had been deprecated since Pyramid 1.1. Instead of
+ passing view-related arguments to ``add_route``, use a separate call to
+ :meth:`pyramid.config.Configurator.add_view` to associate a view with a route
+ using its ``route_name`` argument. Note that this impacts the
+ :meth:`pyramid.config.Configurator.add_static_view` function too, because
+ it delegates to``add_route``.
+
+- Removed the ability to influence and query a :class:`pyramid.request.Request`
+ object as if it were a dictionary. Previously it was possible to use methods
+ like ``__getitem__``, ``get``, ``items``, and other dictlike methods to
+ access values in the WSGI environment. This behavior had been deprecated
+ since Pyramid 1.1. Use methods of ``request.environ`` (a real dictionary)
+ instead.
+
+- Removed ancient backwards compatibily hack in
+ ``pyramid.traversal.DefaultRootFactory`` which populated the ``__dict__`` of
+ the factory with the matchdict values for compatibility with BFG 0.9.
+
+- The ``renderer_globals_factory`` argument to the
+ :class:`pyramid.config.Configurator` constructor and the
+ coresponding argument to :meth:`~pyramid.config.Configurator.setup_registry`
+ has been removed. The ``set_renderer_globals_factory`` method of
+ :class:`~pyramid.config.Configurator` has also been removed. The (internal)
+ ``pyramid.interfaces.IRendererGlobals`` interface was also removed. These
+ arguments, methods and interfaces had been deprecated since 1.1. Use a
+ ``BeforeRender`` event subscriber as documented in the "Hooks" chapter of the
+ Pyramid narrative documentation instead of providing renderer globals values
+ to the configurator.
+
+
+Deprecations
+------------
+
+- Returning a ``("defname", dict)`` tuple from a view which has a Mako renderer
+ is now deprecated. Instead you should use the renderer spelling
+ ``foo#defname.mak`` in the view configuration definition and return a dict
+ only.
+
+- The :meth:`pyramid.config.Configurator.set_request_property` method now issues
+ a deprecation warning when used. It had been docs-deprecated in 1.4
+ but did not issue a deprecation warning when used.
+
+Documentation Enhancements
+--------------------------
+
+- A new documentation chapter named :ref:`quick_tour` was added. It describes
+ starting out with Pyramid from a high level.
+
+- Many other enhancements.
+
+
+Dependency Changes
+------------------
+
+- Pyramid no longer depends upon ``Mako`` or ``Chameleon``.
+